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.c298
-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.c400
-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.c435
-rw-r--r--usr.sbin/IPXrouted/timer.c239
-rw-r--r--usr.sbin/IPXrouted/trace.c521
-rw-r--r--usr.sbin/IPXrouted/trace.h138
-rw-r--r--usr.sbin/Makefile330
-rw-r--r--usr.sbin/Makefile.inc4
-rw-r--r--usr.sbin/ac/Makefile25
-rw-r--r--usr.sbin/ac/ac.8153
-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.c91
-rw-r--r--usr.sbin/acpi/Makefile7
-rw-r--r--usr.sbin/acpi/Makefile.inc11
-rw-r--r--usr.sbin/acpi/acpiconf/Makefile7
-rw-r--r--usr.sbin/acpi/acpiconf/acpiconf.896
-rw-r--r--usr.sbin/acpi/acpiconf/acpiconf.c192
-rw-r--r--usr.sbin/acpi/acpidb/Makefile42
-rw-r--r--usr.sbin/acpi/acpidb/acpidb.8167
-rw-r--r--usr.sbin/acpi/acpidb/acpidb.c497
-rw-r--r--usr.sbin/acpi/acpidump/Makefile8
-rw-r--r--usr.sbin/acpi/acpidump/acpi.c825
-rw-r--r--usr.sbin/acpi/acpidump/acpi_user.c212
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.8190
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.c139
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.h337
-rw-r--r--usr.sbin/acpi/iasl/Makefile48
-rw-r--r--usr.sbin/acpi/iasl/iasl.8174
-rw-r--r--usr.sbin/adduser/Makefile6
-rw-r--r--usr.sbin/adduser/adduser.8473
-rw-r--r--usr.sbin/adduser/adduser.conf.5204
-rw-r--r--usr.sbin/adduser/adduser.sh1003
-rw-r--r--usr.sbin/adduser/rmuser.8210
-rw-r--r--usr.sbin/adduser/rmuser.sh360
-rw-r--r--usr.sbin/amd/Makefile11
-rw-r--r--usr.sbin/amd/Makefile.inc36
-rw-r--r--usr.sbin/amd/NOTES3
-rw-r--r--usr.sbin/amd/amd/Makefile42
-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/Makefile22
-rw-r--r--usr.sbin/amd/hlfsd/Makefile19
-rw-r--r--usr.sbin/amd/include/Makefile19
-rw-r--r--usr.sbin/amd/include/amu_nfs_prot.h1
-rw-r--r--usr.sbin/amd/include/aux_conf.h81
-rw-r--r--usr.sbin/amd/include/build_version.h6
-rw-r--r--usr.sbin/amd/include/config.h2037
-rw-r--r--usr.sbin/amd/include/newvers.sh43
-rw-r--r--usr.sbin/amd/libamu/Makefile31
-rw-r--r--usr.sbin/amd/mk-amd-map/Makefile13
-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.c1833
-rw-r--r--usr.sbin/apm/Makefile9
-rw-r--r--usr.sbin/apm/apm.8148
-rw-r--r--usr.sbin/apm/apm.c505
-rw-r--r--usr.sbin/apmd/Makefile21
-rw-r--r--usr.sbin/apmd/README213
-rw-r--r--usr.sbin/apmd/apmd.8314
-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/arlcontrol/Makefile11
-rw-r--r--usr.sbin/arlcontrol/arlcontrol.8166
-rw-r--r--usr.sbin/arlcontrol/arlcontrol.c431
-rw-r--r--usr.sbin/arp/Makefile9
-rw-r--r--usr.sbin/arp/arp.4176
-rw-r--r--usr.sbin/arp/arp.8187
-rw-r--r--usr.sbin/arp/arp.c798
-rw-r--r--usr.sbin/asf/Makefile6
-rw-r--r--usr.sbin/asf/asf.8112
-rw-r--r--usr.sbin/asf/asf.c329
-rw-r--r--usr.sbin/atm/Makefile29
-rw-r--r--usr.sbin/atm/Makefile.inc26
-rw-r--r--usr.sbin/atm/atmarpd/Makefile36
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_config.c127
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_log.c147
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_scsp.c778
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_subr.c953
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_timer.c229
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_var.h224
-rw-r--r--usr.sbin/atm/atmarpd/atmarpd.8172
-rw-r--r--usr.sbin/atm/atmarpd/atmarpd.c412
-rw-r--r--usr.sbin/atm/scspd/Makefile41
-rw-r--r--usr.sbin/atm/scspd/scsp_cafsm.c1439
-rw-r--r--usr.sbin/atm/scspd/scsp_config.c1158
-rw-r--r--usr.sbin/atm/scspd/scsp_config_lex.c530
-rw-r--r--usr.sbin/atm/scspd/scsp_config_parse.y412
-rw-r--r--usr.sbin/atm/scspd/scsp_hfsm.c577
-rw-r--r--usr.sbin/atm/scspd/scsp_if.c626
-rw-r--r--usr.sbin/atm/scspd/scsp_if.h194
-rw-r--r--usr.sbin/atm/scspd/scsp_input.c1088
-rw-r--r--usr.sbin/atm/scspd/scsp_log.c264
-rw-r--r--usr.sbin/atm/scspd/scsp_msg.c590
-rw-r--r--usr.sbin/atm/scspd/scsp_msg.h461
-rw-r--r--usr.sbin/atm/scspd/scsp_output.c930
-rw-r--r--usr.sbin/atm/scspd/scsp_print.c1301
-rw-r--r--usr.sbin/atm/scspd/scsp_socket.c1344
-rw-r--r--usr.sbin/atm/scspd/scsp_subr.c1113
-rw-r--r--usr.sbin/atm/scspd/scsp_timer.c265
-rw-r--r--usr.sbin/atm/scspd/scsp_var.h461
-rw-r--r--usr.sbin/atm/scspd/scspd.8633
-rw-r--r--usr.sbin/atm/scspd/scspd.c547
-rw-r--r--usr.sbin/authpf/Makefile26
-rw-r--r--usr.sbin/bluetooth/Makefile17
-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/Makefile9
-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.c305
-rw-r--r--usr.sbin/bluetooth/bt3cfw/Makefile12
-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.899
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c208
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h50
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/hid.c209
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/sdp.c432
-rw-r--r--usr.sbin/bluetooth/bthidd/Makefile14
-rw-r--r--usr.sbin/bluetooth/bthidd/bthid_config.h67
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.c256
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.conf.sample72
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.h88
-rw-r--r--usr.sbin/bluetooth/bthidd/client.c244
-rw-r--r--usr.sbin/bluetooth/bthidd/hid.c262
-rw-r--r--usr.sbin/bluetooth/bthidd/lexer.l100
-rw-r--r--usr.sbin/bluetooth/bthidd/parser.y447
-rw-r--r--usr.sbin/bluetooth/bthidd/server.c316
-rw-r--r--usr.sbin/bluetooth/bthidd/session.c136
-rw-r--r--usr.sbin/bluetooth/hccontrol/Makefile14
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.8176
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.c274
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.h76
-rw-r--r--usr.sbin/bluetooth/hccontrol/host_controller_baseband.c1877
-rw-r--r--usr.sbin/bluetooth/hccontrol/info.c215
-rw-r--r--usr.sbin/bluetooth/hccontrol/link_control.c966
-rw-r--r--usr.sbin/bluetooth/hccontrol/link_policy.c305
-rw-r--r--usr.sbin/bluetooth/hccontrol/node.c573
-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.c378
-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.y432
-rw-r--r--usr.sbin/bluetooth/hcseriald/Makefile12
-rw-r--r--usr.sbin/bluetooth/hcseriald/hcseriald.886
-rw-r--r--usr.sbin/bluetooth/hcseriald/hcseriald.c281
-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/Makefile12
-rw-r--r--usr.sbin/bluetooth/l2ping/l2ping.8101
-rw-r--r--usr.sbin/bluetooth/l2ping/l2ping.c275
-rw-r--r--usr.sbin/bluetooth/rfcomm_pppd/Makefile14
-rw-r--r--usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.8303
-rw-r--r--usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c407
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/Makefile12
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/sdpcontrol.8114
-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.c707
-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/irmc.c133
-rw-r--r--usr.sbin/bluetooth/sdpd/irmc_command.c117
-rw-r--r--usr.sbin/bluetooth/sdpd/lan.c177
-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/opush.c133
-rw-r--r--usr.sbin/bluetooth/sdpd/profile.c382
-rw-r--r--usr.sbin/bluetooth/sdpd/profile.h90
-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.c91
-rw-r--r--usr.sbin/bluetooth/sdpd/sd.c212
-rw-r--r--usr.sbin/bluetooth/sdpd/sdpd.8136
-rw-r--r--usr.sbin/bluetooth/sdpd/server.c547
-rw-r--r--usr.sbin/bluetooth/sdpd/server.h101
-rw-r--r--usr.sbin/bluetooth/sdpd/sp.c117
-rw-r--r--usr.sbin/bluetooth/sdpd/srr.c138
-rw-r--r--usr.sbin/bluetooth/sdpd/ssar.c225
-rw-r--r--usr.sbin/bluetooth/sdpd/ssr.c255
-rw-r--r--usr.sbin/bluetooth/sdpd/sur.c82
-rw-r--r--usr.sbin/boot0cfg/Makefile8
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.8182
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.c449
-rw-r--r--usr.sbin/boot98cfg/Makefile8
-rw-r--r--usr.sbin/boot98cfg/boot98cfg.899
-rw-r--r--usr.sbin/boot98cfg/boot98cfg.c295
-rw-r--r--usr.sbin/bootparamd/Makefile5
-rw-r--r--usr.sbin/bootparamd/Makefile.inc4
-rw-r--r--usr.sbin/bootparamd/bootparamd/Makefile24
-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.c344
-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/Makefile6
-rw-r--r--usr.sbin/bsnmpd/Makefile.inc3
-rw-r--r--usr.sbin/bsnmpd/bsnmpd/Makefile43
-rw-r--r--usr.sbin/bsnmpd/gensnmptree/Makefile12
-rw-r--r--usr.sbin/bsnmpd/modules/Makefile11
-rw-r--r--usr.sbin/bsnmpd/modules/Makefile.inc29
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_mibII/Makefile19
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt398
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile19
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def78
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3417
-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/btxld/Makefile8
-rw-r--r--usr.sbin/btxld/btx.h68
-rw-r--r--usr.sbin/btxld/btxld.897
-rw-r--r--usr.sbin/btxld/btxld.c566
-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.8206
-rw-r--r--usr.sbin/burncd/burncd.c695
-rw-r--r--usr.sbin/cdcontrol/Makefile10
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.1214
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.c1262
-rw-r--r--usr.sbin/chkgrp/Makefile8
-rw-r--r--usr.sbin/chkgrp/chkgrp.884
-rw-r--r--usr.sbin/chkgrp/chkgrp.c146
-rw-r--r--usr.sbin/chown/Makefile10
-rw-r--r--usr.sbin/chown/chgrp.1145
-rw-r--r--usr.sbin/chown/chown.8170
-rw-r--r--usr.sbin/chown/chown.c311
-rw-r--r--usr.sbin/chroot/Makefile9
-rw-r--r--usr.sbin/chroot/chroot.898
-rw-r--r--usr.sbin/chroot/chroot.c187
-rw-r--r--usr.sbin/ckdist/Makefile11
-rw-r--r--usr.sbin/ckdist/ckdist.1101
-rw-r--r--usr.sbin/ckdist/ckdist.c445
-rw-r--r--usr.sbin/config/Makefile17
-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.5348
-rw-r--r--usr.sbin/config/config.8261
-rw-r--r--usr.sbin/config/config.h175
-rw-r--r--usr.sbin/config/config.y364
-rw-r--r--usr.sbin/config/configvers.h11
-rw-r--r--usr.sbin/config/lang.l278
-rw-r--r--usr.sbin/config/main.c485
-rw-r--r--usr.sbin/config/mkheaders.c237
-rw-r--r--usr.sbin/config/mkmakefile.c794
-rw-r--r--usr.sbin/config/mkoptions.c370
-rw-r--r--usr.sbin/cron/Makefile5
-rw-r--r--usr.sbin/cron/Makefile.inc9
-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.8177
-rw-r--r--usr.sbin/cron/cron/cron.c449
-rw-r--r--usr.sbin/cron/cron/cron.h296
-rw-r--r--usr.sbin/cron/cron/database.c263
-rw-r--r--usr.sbin/cron/cron/do_command.c556
-rw-r--r--usr.sbin/cron/cron/externs.h145
-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.c240
-rw-r--r--usr.sbin/cron/cron/user.c128
-rw-r--r--usr.sbin/cron/crontab/Makefile16
-rw-r--r--usr.sbin/cron/crontab/crontab.1141
-rw-r--r--usr.sbin/cron/crontab/crontab.5280
-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/Makefile10
-rw-r--r--usr.sbin/cron/lib/compat.c237
-rw-r--r--usr.sbin/cron/lib/entry.c636
-rw-r--r--usr.sbin/cron/lib/env.c269
-rw-r--r--usr.sbin/cron/lib/misc.c662
-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.c119
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.1448
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.c1128
-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.186
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.c268
-rw-r--r--usr.sbin/crunch/crunchide/exec_aout.c197
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf32.c425
-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.1301
-rw-r--r--usr.sbin/ctm/ctm/ctm.5181
-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.1487
-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/Makefile27
-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/daemon/Makefile8
-rw-r--r--usr.sbin/daemon/daemon.881
-rw-r--r--usr.sbin/daemon/daemon.c112
-rw-r--r--usr.sbin/dconschat/Makefile12
-rw-r--r--usr.sbin/dconschat/dconschat.8314
-rw-r--r--usr.sbin/dconschat/dconschat.c964
-rw-r--r--usr.sbin/devinfo/Makefile11
-rw-r--r--usr.sbin/devinfo/devinfo.872
-rw-r--r--usr.sbin/devinfo/devinfo.c229
-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.869
-rw-r--r--usr.sbin/diskinfo/diskinfo.c310
-rw-r--r--usr.sbin/editmap/Makefile49
-rw-r--r--usr.sbin/edquota/Makefile9
-rw-r--r--usr.sbin/edquota/edquota.8247
-rw-r--r--usr.sbin/edquota/edquota.c849
-rw-r--r--usr.sbin/edquota/pathnames.h39
-rw-r--r--usr.sbin/eeprom/Makefile12
-rw-r--r--usr.sbin/eeprom/eeprom.8668
-rw-r--r--usr.sbin/eeprom/eeprom.c153
-rw-r--r--usr.sbin/eeprom/ofw_options.c314
-rw-r--r--usr.sbin/eeprom/ofw_options.h34
-rw-r--r--usr.sbin/elf2exe/Makefile7
-rw-r--r--usr.sbin/elf2exe/elf2exe.856
-rw-r--r--usr.sbin/elf2exe/elf2exe.c403
-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.c907
-rw-r--r--usr.sbin/faithd/faithd.h70
-rw-r--r--usr.sbin/faithd/ftp.c1083
-rw-r--r--usr.sbin/faithd/prefix.c349
-rw-r--r--usr.sbin/faithd/prefix.h52
-rw-r--r--usr.sbin/faithd/tcp.c323
-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.c218
-rw-r--r--usr.sbin/fdformat/Makefile15
-rw-r--r--usr.sbin/fdformat/fdformat.1178
-rw-r--r--usr.sbin/fdformat/fdformat.c371
-rw-r--r--usr.sbin/fdread/Makefile10
-rw-r--r--usr.sbin/fdread/fdread.1222
-rw-r--r--usr.sbin/fdread/fdread.c337
-rw-r--r--usr.sbin/fdread/fdutil.c530
-rw-r--r--usr.sbin/fdread/fdutil.h37
-rw-r--r--usr.sbin/fdwrite/Makefile12
-rw-r--r--usr.sbin/fdwrite/fdwrite.1127
-rw-r--r--usr.sbin/fdwrite/fdwrite.c198
-rw-r--r--usr.sbin/fwcontrol/Makefile12
-rw-r--r--usr.sbin/fwcontrol/fwcontrol.8146
-rw-r--r--usr.sbin/fwcontrol/fwcontrol.c689
-rw-r--r--usr.sbin/fwcontrol/fwdv.c410
-rw-r--r--usr.sbin/getfmac/Makefile9
-rw-r--r--usr.sbin/getfmac/getfmac.857
-rw-r--r--usr.sbin/getfmac/getfmac.c116
-rw-r--r--usr.sbin/getpmac/Makefile9
-rw-r--r--usr.sbin/getpmac/getpmac.859
-rw-r--r--usr.sbin/getpmac/getpmac.c127
-rw-r--r--usr.sbin/gstat/Makefile10
-rw-r--r--usr.sbin/gstat/gstat.890
-rw-r--r--usr.sbin/gstat/gstat.c266
-rw-r--r--usr.sbin/i4b/Makefile7
-rw-r--r--usr.sbin/i4b/Makefile.inc16
-rw-r--r--usr.sbin/i4b/dtmfdecode/Makefile19
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfdecode.169
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfdecode.c152
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu2098
-rw-r--r--usr.sbin/i4b/g711conv/Makefile11
-rw-r--r--usr.sbin/i4b/g711conv/g711conv.192
-rw-r--r--usr.sbin/i4b/g711conv/g711conv.c307
-rw-r--r--usr.sbin/i4b/isdnd/Makefile37
-rw-r--r--usr.sbin/i4b/isdnd/alias.c193
-rw-r--r--usr.sbin/i4b/isdnd/config.h61
-rw-r--r--usr.sbin/i4b/isdnd/controller.c525
-rw-r--r--usr.sbin/i4b/isdnd/curses.c891
-rw-r--r--usr.sbin/i4b/isdnd/dial.c161
-rw-r--r--usr.sbin/i4b/isdnd/exec.c407
-rw-r--r--usr.sbin/i4b/isdnd/fsm.c446
-rw-r--r--usr.sbin/i4b/isdnd/holiday.c198
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.8428
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.acct.5107
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.h906
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.rates.5113
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.rc.51115
-rw-r--r--usr.sbin/i4b/isdnd/log.c248
-rw-r--r--usr.sbin/i4b/isdnd/main.c840
-rw-r--r--usr.sbin/i4b/isdnd/monitor.c1284
-rw-r--r--usr.sbin/i4b/isdnd/msghdl.c1350
-rw-r--r--usr.sbin/i4b/isdnd/pathnames.h61
-rw-r--r--usr.sbin/i4b/isdnd/pcause.c230
-rw-r--r--usr.sbin/i4b/isdnd/process.c219
-rw-r--r--usr.sbin/i4b/isdnd/rates.c509
-rw-r--r--usr.sbin/i4b/isdnd/rc_config.c1865
-rw-r--r--usr.sbin/i4b/isdnd/rc_parse.y539
-rw-r--r--usr.sbin/i4b/isdnd/rc_scan.l209
-rw-r--r--usr.sbin/i4b/isdnd/support.c1169
-rw-r--r--usr.sbin/i4b/isdnd/timer.c447
-rw-r--r--usr.sbin/i4b/isdndebug/Makefile7
-rw-r--r--usr.sbin/i4b/isdndebug/isdndebug.8109
-rw-r--r--usr.sbin/i4b/isdndebug/main.c633
-rw-r--r--usr.sbin/i4b/isdndecode/Makefile8
-rw-r--r--usr.sbin/i4b/isdndecode/decode.h76
-rw-r--r--usr.sbin/i4b/isdndecode/facility.c1111
-rw-r--r--usr.sbin/i4b/isdndecode/facility.h180
-rw-r--r--usr.sbin/i4b/isdndecode/isdndecode.8173
-rw-r--r--usr.sbin/i4b/isdndecode/layer1.c82
-rw-r--r--usr.sbin/i4b/isdndecode/layer2.c300
-rw-r--r--usr.sbin/i4b/isdndecode/layer3.c516
-rw-r--r--usr.sbin/i4b/isdndecode/layer3_subr.c1122
-rw-r--r--usr.sbin/i4b/isdndecode/main.c794
-rw-r--r--usr.sbin/i4b/isdndecode/pcause.c330
-rw-r--r--usr.sbin/i4b/isdndecode/pcause.h111
-rw-r--r--usr.sbin/i4b/isdnmonitor/Makefile17
-rw-r--r--usr.sbin/i4b/isdnmonitor/curses.c624
-rw-r--r--usr.sbin/i4b/isdnmonitor/isdnmonitor.8174
-rw-r--r--usr.sbin/i4b/isdnmonitor/main.c1196
-rw-r--r--usr.sbin/i4b/isdnmonitor/monitor.h299
-rw-r--r--usr.sbin/i4b/isdnmonitor/monprivate.h210
-rw-r--r--usr.sbin/i4b/isdnphone/Makefile10
-rw-r--r--usr.sbin/i4b/isdnphone/audio.c159
-rw-r--r--usr.sbin/i4b/isdnphone/defs.h189
-rw-r--r--usr.sbin/i4b/isdnphone/display.c246
-rw-r--r--usr.sbin/i4b/isdnphone/isdn.c191
-rw-r--r--usr.sbin/i4b/isdnphone/isdnphone.888
-rw-r--r--usr.sbin/i4b/isdnphone/main.c533
-rw-r--r--usr.sbin/i4b/isdntel/Makefile10
-rw-r--r--usr.sbin/i4b/isdntel/alias.c141
-rw-r--r--usr.sbin/i4b/isdntel/alias.h51
-rw-r--r--usr.sbin/i4b/isdntel/defs.h157
-rw-r--r--usr.sbin/i4b/isdntel/display.c254
-rw-r--r--usr.sbin/i4b/isdntel/files.c308
-rw-r--r--usr.sbin/i4b/isdntel/isdntel.897
-rw-r--r--usr.sbin/i4b/isdntel/main.c399
-rw-r--r--usr.sbin/i4b/isdntelctl/Makefile7
-rw-r--r--usr.sbin/i4b/isdntelctl/isdntelctl.898
-rw-r--r--usr.sbin/i4b/isdntelctl/main.c228
-rw-r--r--usr.sbin/i4b/isdntest/Makefile10
-rw-r--r--usr.sbin/i4b/isdntest/isdntest.8115
-rw-r--r--usr.sbin/i4b/isdntest/main.c745
-rw-r--r--usr.sbin/i4b/isdntrace/1tr6.c756
-rw-r--r--usr.sbin/i4b/isdntrace/Makefile9
-rw-r--r--usr.sbin/i4b/isdntrace/cable.txt62
-rw-r--r--usr.sbin/i4b/isdntrace/isdntrace.8222
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_1tr6.c166
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_1tr6.h70
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_q850.c330
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_q850.h111
-rw-r--r--usr.sbin/i4b/isdntrace/q921.c268
-rw-r--r--usr.sbin/i4b/isdntrace/q931.c800
-rw-r--r--usr.sbin/i4b/isdntrace/q931_util.c1047
-rw-r--r--usr.sbin/i4b/isdntrace/q932_fac.c1236
-rw-r--r--usr.sbin/i4b/isdntrace/q932_fac.h180
-rw-r--r--usr.sbin/i4b/isdntrace/trace.c848
-rw-r--r--usr.sbin/i4b/isdntrace/trace.h93
-rw-r--r--usr.sbin/i4b/isdntrace/unknownl3.c106
-rw-r--r--usr.sbin/i4b/man/Makefile6
-rw-r--r--usr.sbin/i4b/man/i4b.4110
-rw-r--r--usr.sbin/i4b/man/i4bcapi.459
-rw-r--r--usr.sbin/i4b/man/i4bctl.450
-rw-r--r--usr.sbin/i4b/man/i4bing.463
-rw-r--r--usr.sbin/i4b/man/i4bipr.496
-rw-r--r--usr.sbin/i4b/man/i4bisppp.4123
-rw-r--r--usr.sbin/i4b/man/i4bq921.448
-rw-r--r--usr.sbin/i4b/man/i4bq931.448
-rw-r--r--usr.sbin/i4b/man/i4brbch.449
-rw-r--r--usr.sbin/i4b/man/i4btel.4133
-rw-r--r--usr.sbin/i4b/man/i4btrc.454
-rw-r--r--usr.sbin/i4b/man/iavc.473
-rw-r--r--usr.sbin/i4b/man/ifpi.464
-rw-r--r--usr.sbin/i4b/man/ifpi2.460
-rw-r--r--usr.sbin/i4b/man/ifpnp.467
-rw-r--r--usr.sbin/i4b/man/ihfc.471
-rw-r--r--usr.sbin/i4b/man/isic.4359
-rw-r--r--usr.sbin/i4b/man/itjc.466
-rw-r--r--usr.sbin/i4b/man/iwic.468
-rw-r--r--usr.sbin/ifmcstat/Makefile11
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.858
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.c266
-rw-r--r--usr.sbin/inetd/Makefile22
-rw-r--r--usr.sbin/inetd/builtins.c817
-rw-r--r--usr.sbin/inetd/inetd.8921
-rw-r--r--usr.sbin/inetd/inetd.c2591
-rw-r--r--usr.sbin/inetd/inetd.h151
-rw-r--r--usr.sbin/inetd/pathnames.h40
-rw-r--r--usr.sbin/iostat/Makefile10
-rw-r--r--usr.sbin/iostat/iostat.8434
-rw-r--r--usr.sbin/iostat/iostat.c763
-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/ipftest/Makefile19
-rw-r--r--usr.sbin/ipresend/Makefile17
-rw-r--r--usr.sbin/ipsend/Makefile23
-rw-r--r--usr.sbin/iptest/Makefile16
-rw-r--r--usr.sbin/jail/Makefile10
-rw-r--r--usr.sbin/jail/jail.8546
-rw-r--r--usr.sbin/jail/jail.c126
-rw-r--r--usr.sbin/jexec/Makefile7
-rw-r--r--usr.sbin/jexec/jexec.852
-rw-r--r--usr.sbin/jexec/jexec.c62
-rw-r--r--usr.sbin/jls/Makefile7
-rw-r--r--usr.sbin/jls/jls.850
-rw-r--r--usr.sbin/jls/jls.c77
-rw-r--r--usr.sbin/kbdcontrol/Makefile14
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.1235
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.c1142
-rw-r--r--usr.sbin/kbdcontrol/kbdmap.5313
-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/Makefile8
-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.886
-rw-r--r--usr.sbin/kernbb/kernbb.c145
-rw-r--r--usr.sbin/keyserv/Makefile25
-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.h20
-rw-r--r--usr.sbin/keyserv/setkey.c550
-rw-r--r--usr.sbin/kgmon/Makefile16
-rw-r--r--usr.sbin/kgmon/kgmon.8133
-rw-r--r--usr.sbin/kgmon/kgmon.c538
-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/endian.h52
-rw-r--r--usr.sbin/kgzip/i386_a.out.h139
-rw-r--r--usr.sbin/kgzip/kgz.h57
-rw-r--r--usr.sbin/kgzip/kgzcmp.c235
-rw-r--r--usr.sbin/kgzip/kgzip.8141
-rw-r--r--usr.sbin/kgzip/kgzip.c176
-rw-r--r--usr.sbin/kgzip/kgzip.h51
-rw-r--r--usr.sbin/kgzip/kgzld.c100
-rw-r--r--usr.sbin/kgzip/xio.c121
-rw-r--r--usr.sbin/kldxref/Makefile15
-rw-r--r--usr.sbin/kldxref/ef.c505
-rw-r--r--usr.sbin/kldxref/ef.h50
-rw-r--r--usr.sbin/kldxref/ef_nop.c39
-rw-r--r--usr.sbin/kldxref/ef_sparc64.c63
-rw-r--r--usr.sbin/kldxref/fileformat40
-rw-r--r--usr.sbin/kldxref/kldxref.895
-rw-r--r--usr.sbin/kldxref/kldxref.c356
-rw-r--r--usr.sbin/lastlogin/Makefile8
-rw-r--r--usr.sbin/lastlogin/lastlogin.878
-rw-r--r--usr.sbin/lastlogin/lastlogin.c134
-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.896
-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.c716
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.c875
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.h73
-rw-r--r--usr.sbin/lpr/common_source/displayq.c539
-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.c583
-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.c396
-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.1117
-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.8312
-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.8321
-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.c1897
-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.1140
-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.1318
-rw-r--r--usr.sbin/lpr/lpr/lpr.c891
-rw-r--r--usr.sbin/lpr/lpr/printcap.5426
-rw-r--r--usr.sbin/lpr/lprm/Makefile18
-rw-r--r--usr.sbin/lpr/lprm/lprm.1149
-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.c86
-rw-r--r--usr.sbin/lpr/pac/Makefile14
-rw-r--r--usr.sbin/lpr/pac/pac.8108
-rw-r--r--usr.sbin/lpr/pac/pac.c454
-rw-r--r--usr.sbin/lptcontrol/Makefile8
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.883
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.c95
-rw-r--r--usr.sbin/mailstats/Makefile43
-rw-r--r--usr.sbin/mailwrapper/Makefile31
-rw-r--r--usr.sbin/mailwrapper/mailwrapper.8164
-rw-r--r--usr.sbin/mailwrapper/mailwrapper.c190
-rw-r--r--usr.sbin/mailwrapper/pathnames.h35
-rw-r--r--usr.sbin/makemap/Makefile50
-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/Makefile6
-rw-r--r--usr.sbin/memcontrol/memcontrol.8111
-rw-r--r--usr.sbin/memcontrol/memcontrol.c343
-rw-r--r--usr.sbin/mergemaster/Makefile7
-rw-r--r--usr.sbin/mergemaster/mergemaster.8403
-rwxr-xr-xusr.sbin/mergemaster/mergemaster.sh1112
-rw-r--r--usr.sbin/mixer/Makefile8
-rw-r--r--usr.sbin/mixer/mixer.8172
-rw-r--r--usr.sbin/mixer/mixer.c287
-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.c287
-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.c366
-rw-r--r--usr.sbin/mount_portalfs/Makefile15
-rw-r--r--usr.sbin/mount_portalfs/activate.c212
-rw-r--r--usr.sbin/mount_portalfs/conf.c338
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.8152
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.c283
-rw-r--r--usr.sbin/mount_portalfs/pathnames.h44
-rw-r--r--usr.sbin/mount_portalfs/portal.conf7
-rw-r--r--usr.sbin/mount_portalfs/portald.h84
-rw-r--r--usr.sbin/mount_portalfs/pt_conf.c53
-rw-r--r--usr.sbin/mount_portalfs/pt_exec.c58
-rw-r--r--usr.sbin/mount_portalfs/pt_file.c107
-rw-r--r--usr.sbin/mount_portalfs/pt_tcp.c165
-rw-r--r--usr.sbin/mount_portalfs/pt_tcplisten.c209
-rw-r--r--usr.sbin/mount_smbfs/Makefile22
-rw-r--r--usr.sbin/mountd/Makefile9
-rw-r--r--usr.sbin/mountd/exports.5361
-rw-r--r--usr.sbin/mountd/mountd.8164
-rw-r--r--usr.sbin/mountd/mountd.c2548
-rw-r--r--usr.sbin/mountd/netgroup.5194
-rw-r--r--usr.sbin/mountd/pathnames.h39
-rw-r--r--usr.sbin/moused/Makefile9
-rw-r--r--usr.sbin/moused/moused.8681
-rw-r--r--usr.sbin/moused/moused.c3026
-rw-r--r--usr.sbin/mptable/Makefile6
-rw-r--r--usr.sbin/mptable/mptable.169
-rw-r--r--usr.sbin/mptable/mptable.c1114
-rw-r--r--usr.sbin/mrouted/LICENSE48
-rw-r--r--usr.sbin/mrouted/Makefile5
-rw-r--r--usr.sbin/mrouted/Makefile.inc11
-rw-r--r--usr.sbin/mrouted/RELEASE493
-rw-r--r--usr.sbin/mrouted/VERSION1
-rw-r--r--usr.sbin/mrouted/callout.c250
-rw-r--r--usr.sbin/mrouted/cfparse.y932
-rw-r--r--usr.sbin/mrouted/common/Makefile12
-rw-r--r--usr.sbin/mrouted/config.c175
-rw-r--r--usr.sbin/mrouted/defs.h413
-rw-r--r--usr.sbin/mrouted/dvmrp.h172
-rw-r--r--usr.sbin/mrouted/icmp.c225
-rw-r--r--usr.sbin/mrouted/igmp.c447
-rw-r--r--usr.sbin/mrouted/igmpv2.h42
-rw-r--r--usr.sbin/mrouted/inet.c235
-rw-r--r--usr.sbin/mrouted/ipip.c145
-rw-r--r--usr.sbin/mrouted/kern.c344
-rw-r--r--usr.sbin/mrouted/main.c1064
-rw-r--r--usr.sbin/mrouted/map-mbone.896
-rw-r--r--usr.sbin/mrouted/map-mbone/Makefile15
-rw-r--r--usr.sbin/mrouted/mapper.c1048
-rw-r--r--usr.sbin/mrouted/mrinfo.891
-rw-r--r--usr.sbin/mrouted/mrinfo.c636
-rw-r--r--usr.sbin/mrouted/mrinfo/Makefile16
-rw-r--r--usr.sbin/mrouted/mrouted.8608
-rw-r--r--usr.sbin/mrouted/mrouted.conf44
-rw-r--r--usr.sbin/mrouted/mrouted/Makefile19
-rw-r--r--usr.sbin/mrouted/mtrace.8544
-rw-r--r--usr.sbin/mrouted/mtrace.c3177
-rw-r--r--usr.sbin/mrouted/mtrace.h89
-rw-r--r--usr.sbin/mrouted/mtrace/Makefile13
-rw-r--r--usr.sbin/mrouted/pathnames.h26
-rw-r--r--usr.sbin/mrouted/prune.c2619
-rw-r--r--usr.sbin/mrouted/prune.h152
-rw-r--r--usr.sbin/mrouted/route.c1475
-rw-r--r--usr.sbin/mrouted/route.h53
-rw-r--r--usr.sbin/mrouted/rsrr.c485
-rw-r--r--usr.sbin/mrouted/rsrr.h139
-rw-r--r--usr.sbin/mrouted/rsrr_var.h41
-rw-r--r--usr.sbin/mrouted/testrsrr/Makefile14
-rw-r--r--usr.sbin/mrouted/testrsrr/testrsrr.c125
-rw-r--r--usr.sbin/mrouted/vif.c1862
-rw-r--r--usr.sbin/mrouted/vif.h237
-rw-r--r--usr.sbin/mtest/Makefile6
-rw-r--r--usr.sbin/mtest/mtest.857
-rw-r--r--usr.sbin/mtest/mtest.c228
-rw-r--r--usr.sbin/mtree/Makefile17
-rw-r--r--usr.sbin/mtree/compare.c367
-rw-r--r--usr.sbin/mtree/create.c415
-rw-r--r--usr.sbin/mtree/excludes.c111
-rw-r--r--usr.sbin/mtree/extern.h59
-rw-r--r--usr.sbin/mtree/misc.c120
-rw-r--r--usr.sbin/mtree/mtree.8376
-rw-r--r--usr.sbin/mtree/mtree.c190
-rw-r--r--usr.sbin/mtree/mtree.h95
-rw-r--r--usr.sbin/mtree/spec.c312
-rw-r--r--usr.sbin/mtree/specspec.c233
-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/verify.c233
-rw-r--r--usr.sbin/named.reload/Makefile15
-rw-r--r--usr.sbin/named.reload/named.reload.870
-rw-r--r--usr.sbin/named.reload/named.reload.sh7
-rw-r--r--usr.sbin/named.restart/Makefile15
-rw-r--r--usr.sbin/named.restart/named.restart.877
-rw-r--r--usr.sbin/named.restart/named.restart.sh13
-rw-r--r--usr.sbin/named/Makefile44
-rw-r--r--usr.sbin/named/Makefile.inc59
-rw-r--r--usr.sbin/named/Makefile.maninc58
-rw-r--r--usr.sbin/ndc/Makefile13
-rw-r--r--usr.sbin/ndiscvt/Makefile23
-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.c725
-rw-r--r--usr.sbin/ndiscvt/inf.h61
-rw-r--r--usr.sbin/ndiscvt/ndiscvt.8168
-rw-r--r--usr.sbin/ndiscvt/ndiscvt.c290
-rw-r--r--usr.sbin/ndp/Makefile26
-rw-r--r--usr.sbin/ndp/gnuc.h2
-rw-r--r--usr.sbin/ndp/ndp.8240
-rw-r--r--usr.sbin/ndp/ndp.c1582
-rw-r--r--usr.sbin/newsyslog/Makefile9
-rw-r--r--usr.sbin/newsyslog/extern.h68
-rw-r--r--usr.sbin/newsyslog/newsyslog.8227
-rw-r--r--usr.sbin/newsyslog/newsyslog.c2367
-rw-r--r--usr.sbin/newsyslog/newsyslog.conf.5362
-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.c850
-rw-r--r--usr.sbin/ngctl/Makefile12
-rw-r--r--usr.sbin/ngctl/config.c104
-rw-r--r--usr.sbin/ngctl/connect.c86
-rw-r--r--usr.sbin/ngctl/debug.c79
-rw-r--r--usr.sbin/ngctl/dot.c195
-rw-r--r--usr.sbin/ngctl/list.c116
-rw-r--r--usr.sbin/ngctl/main.c509
-rw-r--r--usr.sbin/ngctl/mkpeer.c86
-rw-r--r--usr.sbin/ngctl/msg.c155
-rw-r--r--usr.sbin/ngctl/name.c76
-rw-r--r--usr.sbin/ngctl/ngctl.8141
-rw-r--r--usr.sbin/ngctl/ngctl.h102
-rw-r--r--usr.sbin/ngctl/rmhook.c82
-rw-r--r--usr.sbin/ngctl/show.c133
-rw-r--r--usr.sbin/ngctl/shutdown.c75
-rw-r--r--usr.sbin/ngctl/status.c96
-rw-r--r--usr.sbin/ngctl/types.c95
-rw-r--r--usr.sbin/ngctl/write.c111
-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.568
-rw-r--r--usr.sbin/nologin/nologin.861
-rw-r--r--usr.sbin/nologin/nologin.c51
-rw-r--r--usr.sbin/nslookup/Makefile21
-rw-r--r--usr.sbin/nsupdate/Makefile11
-rw-r--r--usr.sbin/ntp/Makefile11
-rw-r--r--usr.sbin/ntp/Makefile.inc23
-rw-r--r--usr.sbin/ntp/config.h1058
-rw-r--r--usr.sbin/ntp/doc/Makefile29
-rw-r--r--usr.sbin/ntp/doc/ntp-genkeys.8208
-rw-r--r--usr.sbin/ntp/doc/ntp.conf.52093
-rw-r--r--usr.sbin/ntp/doc/ntp.keys.5120
-rw-r--r--usr.sbin/ntp/doc/ntpd.8588
-rw-r--r--usr.sbin/ntp/doc/ntpdate.8267
-rw-r--r--usr.sbin/ntp/doc/ntpdc.8725
-rw-r--r--usr.sbin/ntp/doc/ntpq.8749
-rw-r--r--usr.sbin/ntp/doc/ntptime.869
-rw-r--r--usr.sbin/ntp/doc/ntptrace.874
-rw-r--r--usr.sbin/ntp/libntp/Makefile28
-rw-r--r--usr.sbin/ntp/libparse/Makefile15
-rw-r--r--usr.sbin/ntp/ntp-genkeys/Makefile15
-rw-r--r--usr.sbin/ntp/ntpd/Makefile39
-rw-r--r--usr.sbin/ntp/ntpdate/Makefile19
-rw-r--r--usr.sbin/ntp/ntpdc/Makefile22
-rw-r--r--usr.sbin/ntp/ntpq/Makefile21
-rw-r--r--usr.sbin/ntp/ntptime/Makefile14
-rw-r--r--usr.sbin/ntp/ntptimeset/Makefile19
-rw-r--r--usr.sbin/ntp/ntptrace/Makefile19
-rwxr-xr-xusr.sbin/ntp/scripts/mkver46
-rwxr-xr-xusr.sbin/ntp/scripts/ntpver8
-rw-r--r--usr.sbin/ofwdump/Makefile10
-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/pccard/Makefile6
-rw-r--r--usr.sbin/pccard/Makefile.inc5
-rw-r--r--usr.sbin/pccard/pccardc/Makefile14
-rw-r--r--usr.sbin/pccard/pccardc/beep.c77
-rw-r--r--usr.sbin/pccard/pccardc/dumpcis.c114
-rw-r--r--usr.sbin/pccard/pccardc/dumpcisfile.c72
-rw-r--r--usr.sbin/pccard/pccardc/enabler.c153
-rw-r--r--usr.sbin/pccard/pccardc/pccardc.8282
-rw-r--r--usr.sbin/pccard/pccardc/pccardc.c100
-rw-r--r--usr.sbin/pccard/pccardc/pccardmem.c72
-rw-r--r--usr.sbin/pccard/pccardc/power.c82
-rw-r--r--usr.sbin/pccard/pccardc/printcis.c1107
-rw-r--r--usr.sbin/pccard/pccardc/rdattr.c84
-rw-r--r--usr.sbin/pccard/pccardc/rdmap.c109
-rw-r--r--usr.sbin/pccard/pccardc/rdreg.c82
-rw-r--r--usr.sbin/pccard/pccardc/wrattr.c80
-rw-r--r--usr.sbin/pccard/pccardc/wrreg.c73
-rw-r--r--usr.sbin/pccard/pccardd/Makefile15
-rw-r--r--usr.sbin/pccard/pccardd/cardd.c1025
-rw-r--r--usr.sbin/pccard/pccardd/cardd.h211
-rw-r--r--usr.sbin/pccard/pccardd/file.c1096
-rw-r--r--usr.sbin/pccard/pccardd/pccard.conf.5311
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.8189
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.c285
-rw-r--r--usr.sbin/pccard/pccardd/readcis.c790
-rw-r--r--usr.sbin/pccard/pccardd/readcis.h148
-rw-r--r--usr.sbin/pccard/pccardd/server.c188
-rw-r--r--usr.sbin/pccard/pccardd/util.c273
-rw-r--r--usr.sbin/pciconf/Makefile9
-rw-r--r--usr.sbin/pciconf/pathnames.h3
-rw-r--r--usr.sbin/pciconf/pciconf.8222
-rw-r--r--usr.sbin/pciconf/pciconf.c544
-rw-r--r--usr.sbin/pcvt/Makefile9
-rw-r--r--usr.sbin/pcvt/Makefile.inc5
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Acknowledgements111
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Bibliography189
-rw-r--r--usr.sbin/pcvt/Misc/Doc/CharGen149
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Charsets99
-rw-r--r--usr.sbin/pcvt/Misc/Doc/EscapeSequences268
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Keyboard.HP286
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Keyboard.VT231
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Makefile9
-rw-r--r--usr.sbin/pcvt/Misc/Doc/NotesAndHints323
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Makefile7
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Termcap284
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Terminfo41
-rw-r--r--usr.sbin/pcvt/Misc/Etc/pcvt.el19
-rw-r--r--usr.sbin/pcvt/Misc/Etc/pcvt.sh163
-rw-r--r--usr.sbin/pcvt/Misc/Etc/xmodmap-german117
-rw-r--r--usr.sbin/pcvt/Misc/Makefile8
-rw-r--r--usr.sbin/pcvt/Misc/Makefile.inc4
-rw-r--r--usr.sbin/pcvt/Misc/README.FIRST184
-rw-r--r--usr.sbin/pcvt/cursor/Makefile5
-rw-r--r--usr.sbin/pcvt/cursor/cursor.175
-rw-r--r--usr.sbin/pcvt/cursor/cursor.c137
-rw-r--r--usr.sbin/pcvt/demo/Makefile56
-rw-r--r--usr.sbin/pcvt/demo/README20
-rw-r--r--usr.sbin/pcvt/demo/chardemo.vt.gz.uu54
-rw-r--r--usr.sbin/pcvt/demo/colors.vt.gz.uu16
-rw-r--r--usr.sbin/pcvt/demo/cowscene.vt.gz.uu90
-rw-r--r--usr.sbin/pcvt/demo/outerlimit.vt.gz.uu193
-rw-r--r--usr.sbin/pcvt/demo/playvt.c113
-rw-r--r--usr.sbin/pcvt/demo/sgr.vt.gz.uu12
-rw-r--r--usr.sbin/pcvt/demo/twzone.vt.gz.uu350
-rw-r--r--usr.sbin/pcvt/demo/xmas.vt.gz.uu110
-rw-r--r--usr.sbin/pcvt/fed/Makefile9
-rw-r--r--usr.sbin/pcvt/fed/edit.c331
-rw-r--r--usr.sbin/pcvt/fed/fed.159
-rw-r--r--usr.sbin/pcvt/fed/fed.c159
-rw-r--r--usr.sbin/pcvt/fed/fed.h123
-rw-r--r--usr.sbin/pcvt/fed/misc.c344
-rw-r--r--usr.sbin/pcvt/fed/select.c325
-rw-r--r--usr.sbin/pcvt/fontedit/Makefile5
-rw-r--r--usr.sbin/pcvt/fontedit/README36
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.192
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.c926
-rw-r--r--usr.sbin/pcvt/fonts/COPYRIGHT38
-rw-r--r--usr.sbin/pcvt/fonts/Makefile14
-rw-r--r--usr.sbin/pcvt/fonts/vt100pc.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt100sg.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.808.uu49
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.810.uu60
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.816.uu95
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.808.uu49
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.810.uu60
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.816.uu95
-rw-r--r--usr.sbin/pcvt/ispcvt/Makefile6
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.899
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.c250
-rw-r--r--usr.sbin/pcvt/kbdio/Makefile23
-rw-r--r--usr.sbin/pcvt/kbdio/kbdio.y330
-rw-r--r--usr.sbin/pcvt/kbdio/lex.l98
-rw-r--r--usr.sbin/pcvt/kcon/Makefile11
-rw-r--r--usr.sbin/pcvt/kcon/kcon.1129
-rw-r--r--usr.sbin/pcvt/kcon/kcon.c743
-rw-r--r--usr.sbin/pcvt/keycap/Makefile24
-rw-r--r--usr.sbin/pcvt/keycap/keycap.3125
-rw-r--r--usr.sbin/pcvt/keycap/keycap.c383
-rw-r--r--usr.sbin/pcvt/keycap/keycap.h49
-rw-r--r--usr.sbin/pcvt/keycap/keycap.src627
-rw-r--r--usr.sbin/pcvt/keycap/man5/keycap.5129
-rw-r--r--usr.sbin/pcvt/loadfont/Makefile5
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.187
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.c306
-rw-r--r--usr.sbin/pcvt/scon/Makefile5
-rw-r--r--usr.sbin/pcvt/scon/scon.1220
-rw-r--r--usr.sbin/pcvt/scon/scon.c782
-rw-r--r--usr.sbin/pcvt/userkeys/Makefile5
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.1175
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.c287
-rw-r--r--usr.sbin/pcvt/vgaio/CAUTION28
-rw-r--r--usr.sbin/pcvt/vgaio/Makefile24
-rw-r--r--usr.sbin/pcvt/vgaio/lex.l82
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.8141
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.h60
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.y252
-rw-r--r--usr.sbin/pcvt/vttest/Makefile8
-rw-r--r--usr.sbin/pcvt/vttest/README59
-rw-r--r--usr.sbin/pcvt/vttest/esc.c403
-rw-r--r--usr.sbin/pcvt/vttest/header.h45
-rw-r--r--usr.sbin/pcvt/vttest/main.c2018
-rw-r--r--usr.sbin/pcvt/vttest/vttest.121
-rw-r--r--usr.sbin/periodic/Makefile6
-rw-r--r--usr.sbin/periodic/periodic.8252
-rw-r--r--usr.sbin/periodic/periodic.sh106
-rw-r--r--usr.sbin/pkg_install/Makefile9
-rw-r--r--usr.sbin/pkg_install/Makefile.inc19
-rw-r--r--usr.sbin/pkg_install/README8
-rw-r--r--usr.sbin/pkg_install/add/Makefile13
-rw-r--r--usr.sbin/pkg_install/add/add.h44
-rw-r--r--usr.sbin/pkg_install/add/extract.c277
-rw-r--r--usr.sbin/pkg_install/add/futil.c97
-rw-r--r--usr.sbin/pkg_install/add/main.c298
-rw-r--r--usr.sbin/pkg_install/add/perform.c597
-rw-r--r--usr.sbin/pkg_install/add/pkg_add.1539
-rw-r--r--usr.sbin/pkg_install/create/Makefile13
-rw-r--r--usr.sbin/pkg_install/create/create.h54
-rw-r--r--usr.sbin/pkg_install/create/main.c210
-rw-r--r--usr.sbin/pkg_install/create/perform.c476
-rw-r--r--usr.sbin/pkg_install/create/pkg_create.1558
-rw-r--r--usr.sbin/pkg_install/create/pl.c257
-rw-r--r--usr.sbin/pkg_install/delete/Makefile13
-rw-r--r--usr.sbin/pkg_install/delete/delete.h36
-rw-r--r--usr.sbin/pkg_install/delete/main.c157
-rw-r--r--usr.sbin/pkg_install/delete/perform.c381
-rw-r--r--usr.sbin/pkg_install/delete/pkg_delete.1276
-rw-r--r--usr.sbin/pkg_install/info/Makefile13
-rw-r--r--usr.sbin/pkg_install/info/info.h82
-rw-r--r--usr.sbin/pkg_install/info/main.c259
-rw-r--r--usr.sbin/pkg_install/info/perform.c453
-rw-r--r--usr.sbin/pkg_install/info/pkg_info.1241
-rw-r--r--usr.sbin/pkg_install/info/show.c356
-rw-r--r--usr.sbin/pkg_install/lib/Makefile12
-rw-r--r--usr.sbin/pkg_install/lib/deps.c195
-rw-r--r--usr.sbin/pkg_install/lib/exec.c106
-rw-r--r--usr.sbin/pkg_install/lib/file.c426
-rw-r--r--usr.sbin/pkg_install/lib/global.c31
-rw-r--r--usr.sbin/pkg_install/lib/lib.h226
-rw-r--r--usr.sbin/pkg_install/lib/match.c344
-rw-r--r--usr.sbin/pkg_install/lib/msg.c75
-rw-r--r--usr.sbin/pkg_install/lib/pen.c176
-rw-r--r--usr.sbin/pkg_install/lib/pkgwrap.c89
-rw-r--r--usr.sbin/pkg_install/lib/plist.c579
-rw-r--r--usr.sbin/pkg_install/lib/str.c129
-rw-r--r--usr.sbin/pkg_install/lib/url.c144
-rw-r--r--usr.sbin/pkg_install/lib/version.c171
-rw-r--r--usr.sbin/pkg_install/sign/Makefile15
-rw-r--r--usr.sbin/pkg_install/sign/README55
-rw-r--r--usr.sbin/pkg_install/sign/check.c119
-rw-r--r--usr.sbin/pkg_install/sign/common.c90
-rw-r--r--usr.sbin/pkg_install/sign/extern.h100
-rw-r--r--usr.sbin/pkg_install/sign/gzip.c319
-rw-r--r--usr.sbin/pkg_install/sign/gzip.h95
-rw-r--r--usr.sbin/pkg_install/sign/main.c185
-rw-r--r--usr.sbin/pkg_install/sign/pgp.h25
-rw-r--r--usr.sbin/pkg_install/sign/pgp_check.c196
-rw-r--r--usr.sbin/pkg_install/sign/pgp_sign.c280
-rw-r--r--usr.sbin/pkg_install/sign/pkg_sign.1209
-rw-r--r--usr.sbin/pkg_install/sign/sha1.c224
-rw-r--r--usr.sbin/pkg_install/sign/sign.c146
-rw-r--r--usr.sbin/pkg_install/sign/stand.c57
-rw-r--r--usr.sbin/pkg_install/sign/stand.h28
-rw-r--r--usr.sbin/pkg_install/sign/x509.c427
-rwxr-xr-xusr.sbin/pkg_install/tkpkg177
-rw-r--r--usr.sbin/pkg_install/version/Makefile16
-rw-r--r--usr.sbin/pkg_install/version/main.c89
-rw-r--r--usr.sbin/pkg_install/version/perform.c313
-rw-r--r--usr.sbin/pkg_install/version/pkg_version.1206
-rwxr-xr-xusr.sbin/pkg_install/version/test-pkg_version.sh75
-rw-r--r--usr.sbin/pkg_install/version/version.h44
-rw-r--r--usr.sbin/pnpinfo/Makefile20
-rw-r--r--usr.sbin/ppp/Makefile116
-rw-r--r--usr.sbin/ppp/README.changes140
-rw-r--r--usr.sbin/ppp/README.devel49
-rw-r--r--usr.sbin/ppp/README.nat378
-rw-r--r--usr.sbin/ppp/acf.c115
-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.c216
-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.c481
-rw-r--r--usr.sbin/ppp/auth.h70
-rw-r--r--usr.sbin/ppp/bundle.c2009
-rw-r--r--usr.sbin/ppp/bundle.h211
-rw-r--r--usr.sbin/ppp/cbcp.c759
-rw-r--r--usr.sbin/ppp/cbcp.h65
-rw-r--r--usr.sbin/ppp/ccp.c819
-rw-r--r--usr.sbin/ppp/ccp.h165
-rw-r--r--usr.sbin/ppp/chap.c967
-rw-r--r--usr.sbin/ppp/chap.h75
-rw-r--r--usr.sbin/ppp/chap_ms.c417
-rw-r--r--usr.sbin/ppp/chap_ms.h53
-rw-r--r--usr.sbin/ppp/chat.c794
-rw-r--r--usr.sbin/ppp/chat.h82
-rw-r--r--usr.sbin/ppp/command.c3203
-rw-r--r--usr.sbin/ppp/command.h75
-rw-r--r--usr.sbin/ppp/datalink.c1470
-rw-r--r--usr.sbin/ppp/datalink.h157
-rw-r--r--usr.sbin/ppp/deflate.c601
-rw-r--r--usr.sbin/ppp/deflate.h30
-rw-r--r--usr.sbin/ppp/defs.c443
-rw-r--r--usr.sbin/ppp/defs.h142
-rw-r--r--usr.sbin/ppp/descriptor.h53
-rw-r--r--usr.sbin/ppp/ether.c722
-rw-r--r--usr.sbin/ppp/ether.h37
-rw-r--r--usr.sbin/ppp/exec.c233
-rw-r--r--usr.sbin/ppp/exec.h35
-rw-r--r--usr.sbin/ppp/filter.c606
-rw-r--r--usr.sbin/ppp/filter.h101
-rw-r--r--usr.sbin/ppp/fsm.c1209
-rw-r--r--usr.sbin/ppp/fsm.h201
-rw-r--r--usr.sbin/ppp/hdlc.c440
-rw-r--r--usr.sbin/ppp/hdlc.h111
-rw-r--r--usr.sbin/ppp/i4b.c451
-rw-r--r--usr.sbin/ppp/i4b.h37
-rw-r--r--usr.sbin/ppp/id.c303
-rw-r--r--usr.sbin/ppp/id.h91
-rw-r--r--usr.sbin/ppp/iface.c719
-rw-r--r--usr.sbin/ppp/iface.h65
-rw-r--r--usr.sbin/ppp/ip.c985
-rw-r--r--usr.sbin/ppp/ip.h44
-rw-r--r--usr.sbin/ppp/ipcp.c1476
-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.c782
-rw-r--r--usr.sbin/ppp/ipv6cp.h83
-rw-r--r--usr.sbin/ppp/layer.h52
-rw-r--r--usr.sbin/ppp/lcp.c1292
-rw-r--r--usr.sbin/ppp/lcp.h142
-rw-r--r--usr.sbin/ppp/link.c383
-rw-r--r--usr.sbin/ppp/link.h80
-rw-r--r--usr.sbin/ppp/log.c520
-rw-r--r--usr.sbin/ppp/log.h104
-rw-r--r--usr.sbin/ppp/lqr.c447
-rw-r--r--usr.sbin/ppp/lqr.h71
-rw-r--r--usr.sbin/ppp/main.c675
-rw-r--r--usr.sbin/ppp/main.h32
-rw-r--r--usr.sbin/ppp/mbuf.c440
-rw-r--r--usr.sbin/ppp/mbuf.h118
-rw-r--r--usr.sbin/ppp/mp.c1213
-rw-r--r--usr.sbin/ppp/mp.h146
-rw-r--r--usr.sbin/ppp/mppe.c814
-rw-r--r--usr.sbin/ppp/mppe.h33
-rw-r--r--usr.sbin/ppp/nat_cmd.c594
-rw-r--r--usr.sbin/ppp/nat_cmd.h42
-rw-r--r--usr.sbin/ppp/ncp.c537
-rw-r--r--usr.sbin/ppp/ncp.h101
-rw-r--r--usr.sbin/ppp/ncpaddr.c1013
-rw-r--r--usr.sbin/ppp/ncpaddr.h109
-rw-r--r--usr.sbin/ppp/netgraph.c741
-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.c1131
-rw-r--r--usr.sbin/ppp/physical.h173
-rw-r--r--usr.sbin/ppp/ppp.8.m45982
-rw-r--r--usr.sbin/ppp/pred.c343
-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.c571
-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.c1208
-rw-r--r--usr.sbin/ppp/radius.h118
-rw-r--r--usr.sbin/ppp/route.c919
-rw-r--r--usr.sbin/ppp/route.h73
-rw-r--r--usr.sbin/ppp/server.c413
-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.c589
-rw-r--r--usr.sbin/ppp/slcompress.h149
-rw-r--r--usr.sbin/ppp/sync.c80
-rw-r--r--usr.sbin/ppp/sync.h29
-rw-r--r--usr.sbin/ppp/systems.c482
-rw-r--r--usr.sbin/ppp/systems.h43
-rw-r--r--usr.sbin/ppp/tcp.c211
-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.c756
-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.8213
-rw-r--r--usr.sbin/pppctl/pppctl.c676
-rw-r--r--usr.sbin/pppd/Makefile50
-rw-r--r--usr.sbin/pppd/RELNOTES726
-rw-r--r--usr.sbin/pppd/auth.c1637
-rw-r--r--usr.sbin/pppd/cbcp.c430
-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/fsm.c798
-rw-r--r--usr.sbin/pppd/fsm.h144
-rw-r--r--usr.sbin/pppd/ipcp.c1530
-rw-r--r--usr.sbin/pppd/ipcp.h70
-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.c1717
-rw-r--r--usr.sbin/pppd/options.c2570
-rw-r--r--usr.sbin/pppd/patchlevel.h6
-rw-r--r--usr.sbin/pppd/pathnames.h32
-rw-r--r--usr.sbin/pppd/pppd.81202
-rw-r--r--usr.sbin/pppd/pppd.h493
-rw-r--r--usr.sbin/pppd/sys-bsd.c1569
-rw-r--r--usr.sbin/pppd/upap.c618
-rw-r--r--usr.sbin/pppd/upap.h87
-rw-r--r--usr.sbin/pppstats/Makefile9
-rw-r--r--usr.sbin/pppstats/pppstats.8218
-rw-r--r--usr.sbin/pppstats/pppstats.c519
-rw-r--r--usr.sbin/praliases/Makefile50
-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.8267
-rw-r--r--usr.sbin/pstat/pstat.c593
-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.c127
-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.8968
-rw-r--r--usr.sbin/pw/pw.c453
-rw-r--r--usr.sbin/pw/pw.conf.5309
-rw-r--r--usr.sbin/pw/pw.h131
-rw-r--r--usr.sbin/pw/pw_conf.c501
-rw-r--r--usr.sbin/pw/pw_group.c359
-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.c1313
-rw-r--r--usr.sbin/pw/pw_vpw.c316
-rw-r--r--usr.sbin/pw/pwupd.c212
-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.8170
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.c757
-rw-r--r--usr.sbin/quot/Makefile8
-rw-r--r--usr.sbin/quot/quot.8109
-rw-r--r--usr.sbin/quot/quot.c650
-rw-r--r--usr.sbin/quotaon/Makefile11
-rw-r--r--usr.sbin/quotaon/quotaon.8141
-rw-r--r--usr.sbin/quotaon/quotaon.c264
-rw-r--r--usr.sbin/rarpd/Makefile11
-rw-r--r--usr.sbin/rarpd/rarpd.8151
-rw-r--r--usr.sbin/rarpd/rarpd.c956
-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.8110
-rw-r--r--usr.sbin/repquota/repquota.c412
-rw-r--r--usr.sbin/rip6query/Makefile10
-rw-r--r--usr.sbin/rip6query/rip6query.864
-rw-r--r--usr.sbin/rip6query/rip6query.c208
-rw-r--r--usr.sbin/rmt/Makefile14
-rw-r--r--usr.sbin/rmt/rmt.8222
-rw-r--r--usr.sbin/rmt/rmt.c258
-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.8253
-rw-r--r--usr.sbin/route6d/route6d.c3559
-rw-r--r--usr.sbin/route6d/route6d.h81
-rw-r--r--usr.sbin/rpc.lockd/Makefile29
-rw-r--r--usr.sbin/rpc.lockd/kern.c607
-rw-r--r--usr.sbin/rpc.lockd/lock_proc.c1415
-rw-r--r--usr.sbin/rpc.lockd/lockd.c278
-rw-r--r--usr.sbin/rpc.lockd/lockd.h41
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.c2262
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.h25
-rw-r--r--usr.sbin/rpc.lockd/rpc.lockd.8125
-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.c331
-rw-r--r--usr.sbin/rpc.statd/procs.c424
-rw-r--r--usr.sbin/rpc.statd/rpc.statd.8109
-rw-r--r--usr.sbin/rpc.statd/statd.c140
-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.c264
-rw-r--r--usr.sbin/rpc.yppasswdd/Makefile64
-rw-r--r--usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8358
-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.c350
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_server.c900
-rw-r--r--usr.sbin/rpc.yppasswdd/yppwupdate34
-rw-r--r--usr.sbin/rpc.ypupdated/Makefile32
-rw-r--r--usr.sbin/rpc.ypupdated/update.c331
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbdelete.c68
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbupdate.c147
-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.8150
-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/Makefile21
-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.8144
-rw-r--r--usr.sbin/rpcbind/rpcbind.c759
-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/Makefile26
-rw-r--r--usr.sbin/rtadvd/advcap.c455
-rw-r--r--usr.sbin/rtadvd/advcap.h46
-rw-r--r--usr.sbin/rtadvd/config.c1049
-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.8196
-rw-r--r--usr.sbin/rtadvd/rtadvd.c1690
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf21
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf.5426
-rw-r--r--usr.sbin/rtadvd/rtadvd.h161
-rw-r--r--usr.sbin/rtadvd/timer.c208
-rw-r--r--usr.sbin/rtadvd/timer.h65
-rw-r--r--usr.sbin/rtprio/Makefile8
-rw-r--r--usr.sbin/rtprio/rtprio.1210
-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.c383
-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.8272
-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.8232
-rw-r--r--usr.sbin/rwhod/rwhod.c743
-rw-r--r--usr.sbin/sa/Makefile9
-rw-r--r--usr.sbin/sa/extern.h97
-rw-r--r--usr.sbin/sa/main.c554
-rw-r--r--usr.sbin/sa/pathnames.h35
-rw-r--r--usr.sbin/sa/pdb.c422
-rw-r--r--usr.sbin/sa/sa.8237
-rw-r--r--usr.sbin/sa/usrdb.c286
-rw-r--r--usr.sbin/sade/Makefile98
-rw-r--r--usr.sbin/sade/command.c184
-rw-r--r--usr.sbin/sade/config.c1253
-rw-r--r--usr.sbin/sade/devices.c582
-rw-r--r--usr.sbin/sade/disks.c996
-rw-r--r--usr.sbin/sade/dispatch.c442
-rw-r--r--usr.sbin/sade/dmenu.c319
-rw-r--r--usr.sbin/sade/globals.c73
-rw-r--r--usr.sbin/sade/help/partition.hlp169
-rw-r--r--usr.sbin/sade/help/slice.hlp65
-rw-r--r--usr.sbin/sade/install.c1261
-rw-r--r--usr.sbin/sade/keymap.c95
-rw-r--r--usr.sbin/sade/label.c1706
-rw-r--r--usr.sbin/sade/list.h60
-rw-r--r--usr.sbin/sade/main.c187
-rw-r--r--usr.sbin/sade/menus.c2283
-rw-r--r--usr.sbin/sade/misc.c529
-rw-r--r--usr.sbin/sade/msg.c356
-rw-r--r--usr.sbin/sade/rtermcap.c15
-rw-r--r--usr.sbin/sade/sade.81010
-rw-r--r--usr.sbin/sade/sade.h885
-rw-r--r--usr.sbin/sade/system.c542
-rw-r--r--usr.sbin/sade/termcap.c150
-rw-r--r--usr.sbin/sade/usb.c44
-rw-r--r--usr.sbin/sade/variable.c326
-rw-r--r--usr.sbin/sade/wizard.c200
-rw-r--r--usr.sbin/sendmail/Makefile81
-rw-r--r--usr.sbin/setfmac/Makefile10
-rw-r--r--usr.sbin/setfmac/setfmac.866
-rw-r--r--usr.sbin/setfmac/setfmac.c506
-rw-r--r--usr.sbin/setfmac/setfsmac.8128
-rw-r--r--usr.sbin/setkey/Makefile62
-rw-r--r--usr.sbin/setkey/parse.y1267
-rw-r--r--usr.sbin/setkey/sample.cf219
-rw-r--r--usr.sbin/setkey/scriptdump.pl56
-rw-r--r--usr.sbin/setkey/setkey.8692
-rw-r--r--usr.sbin/setkey/setkey.c632
-rw-r--r--usr.sbin/setkey/test-pfkey.c531
-rw-r--r--usr.sbin/setkey/test-policy.c161
-rw-r--r--usr.sbin/setkey/token.l286
-rw-r--r--usr.sbin/setkey/vchar.h36
-rw-r--r--usr.sbin/setpmac/Makefile9
-rw-r--r--usr.sbin/setpmac/setpmac.864
-rw-r--r--usr.sbin/setpmac/setpmac.c92
-rw-r--r--usr.sbin/sicontrol/Makefile8
-rw-r--r--usr.sbin/sicontrol/sicontrol.8109
-rw-r--r--usr.sbin/sicontrol/sicontrol.c572
-rw-r--r--usr.sbin/sliplogin/Makefile11
-rw-r--r--usr.sbin/sliplogin/pathnames.h48
-rw-r--r--usr.sbin/sliplogin/sliplogin.8312
-rw-r--r--usr.sbin/sliplogin/sliplogin.c549
-rw-r--r--usr.sbin/slstat/Makefile9
-rw-r--r--usr.sbin/slstat/slstat.8129
-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.8286
-rw-r--r--usr.sbin/smbmsg/smbmsg.c344
-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/Makefile98
-rw-r--r--usr.sbin/sysinstall/anonFTP.c327
-rw-r--r--usr.sbin/sysinstall/cdrom.c212
-rw-r--r--usr.sbin/sysinstall/command.c184
-rw-r--r--usr.sbin/sysinstall/config.c1253
-rw-r--r--usr.sbin/sysinstall/devices.c582
-rw-r--r--usr.sbin/sysinstall/dhcp.c158
-rw-r--r--usr.sbin/sysinstall/disks.c996
-rw-r--r--usr.sbin/sysinstall/dispatch.c442
-rw-r--r--usr.sbin/sysinstall/dist.c875
-rw-r--r--usr.sbin/sysinstall/dist.h89
-rw-r--r--usr.sbin/sysinstall/dmenu.c319
-rw-r--r--usr.sbin/sysinstall/doc.c123
-rw-r--r--usr.sbin/sysinstall/dos.c94
-rw-r--r--usr.sbin/sysinstall/floppy.c189
-rw-r--r--usr.sbin/sysinstall/ftp.c288
-rw-r--r--usr.sbin/sysinstall/globals.c73
-rw-r--r--usr.sbin/sysinstall/help/anonftp.hlp19
-rw-r--r--usr.sbin/sysinstall/help/configure.hlp10
-rw-r--r--usr.sbin/sysinstall/help/distributions.hlp42
-rw-r--r--usr.sbin/sysinstall/help/drives.hlp92
-rw-r--r--usr.sbin/sysinstall/help/fixit.hlp7
-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.hlp124
-rw-r--r--usr.sbin/sysinstall/help/partition.hlp169
-rw-r--r--usr.sbin/sysinstall/help/register.hlp76
-rw-r--r--usr.sbin/sysinstall/help/securelevel.hlp38
-rw-r--r--usr.sbin/sysinstall/help/shortcuts.hlp114
-rw-r--r--usr.sbin/sysinstall/help/slice.hlp65
-rw-r--r--usr.sbin/sysinstall/help/tcp.hlp34
-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.c824
-rw-r--r--usr.sbin/sysinstall/install.c1261
-rw-r--r--usr.sbin/sysinstall/install.cfg99
-rw-r--r--usr.sbin/sysinstall/installUpgrade.c498
-rw-r--r--usr.sbin/sysinstall/keymap.c95
-rw-r--r--usr.sbin/sysinstall/label.c1706
-rw-r--r--usr.sbin/sysinstall/list.h60
-rw-r--r--usr.sbin/sysinstall/main.c187
-rw-r--r--usr.sbin/sysinstall/media.c882
-rw-r--r--usr.sbin/sysinstall/menus.c2283
-rw-r--r--usr.sbin/sysinstall/misc.c529
-rw-r--r--usr.sbin/sysinstall/modules.c229
-rw-r--r--usr.sbin/sysinstall/mouse.c103
-rw-r--r--usr.sbin/sysinstall/msg.c356
-rw-r--r--usr.sbin/sysinstall/network.c355
-rw-r--r--usr.sbin/sysinstall/nfs.c101
-rw-r--r--usr.sbin/sysinstall/options.c341
-rw-r--r--usr.sbin/sysinstall/package.c263
-rw-r--r--usr.sbin/sysinstall/pccard.c290
-rw-r--r--usr.sbin/sysinstall/rtermcap.c15
-rw-r--r--usr.sbin/sysinstall/sysinstall.81010
-rw-r--r--usr.sbin/sysinstall/sysinstall.h885
-rw-r--r--usr.sbin/sysinstall/system.c542
-rw-r--r--usr.sbin/sysinstall/tape.c127
-rw-r--r--usr.sbin/sysinstall/tcpip.c697
-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/usb.c44
-rw-r--r--usr.sbin/sysinstall/user.c742
-rw-r--r--usr.sbin/sysinstall/variable.c326
-rw-r--r--usr.sbin/sysinstall/wizard.c200
-rw-r--r--usr.sbin/syslogd/Makefile16
-rw-r--r--usr.sbin/syslogd/pathnames.h40
-rw-r--r--usr.sbin/syslogd/syslog.conf.5487
-rw-r--r--usr.sbin/syslogd/syslogd.8331
-rw-r--r--usr.sbin/syslogd/syslogd.c2520
-rw-r--r--usr.sbin/tcpdchk/Makefile19
-rw-r--r--usr.sbin/tcpdmatch/Makefile18
-rw-r--r--usr.sbin/tcpdump/Makefile6
-rw-r--r--usr.sbin/tcpdump/Makefile.inc4
-rw-r--r--usr.sbin/tcpdump/tcpdump/Makefile57
-rw-r--r--usr.sbin/tcpdump/tcpdump/config.h294
-rw-r--r--usr.sbin/tcpdump/tcpslice/Makefile23
-rw-r--r--usr.sbin/tcpdump/tcpslice/gwtm2secs.c72
-rw-r--r--usr.sbin/tcpdump/tcpslice/search.c566
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.1273
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.c626
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.h59
-rw-r--r--usr.sbin/tcpdump/tcpslice/util.c56
-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/Makefile15
-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.h89
-rw-r--r--usr.sbin/timed/timed/globals.h175
-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.8274
-rw-r--r--usr.sbin/timed/timed/timed.c851
-rw-r--r--usr.sbin/timed/timedc/Makefile13
-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.h52
-rw-r--r--usr.sbin/timed/timedc/timedc.8144
-rw-r--r--usr.sbin/timed/timedc/timedc.c261
-rw-r--r--usr.sbin/timed/timedc/timedc.h63
-rw-r--r--usr.sbin/traceroute/Makefile45
-rw-r--r--usr.sbin/traceroute6/Makefile26
-rw-r--r--usr.sbin/traceroute6/traceroute6.8162
-rw-r--r--usr.sbin/traceroute6/traceroute6.c1369
-rw-r--r--usr.sbin/trpt/Makefile12
-rw-r--r--usr.sbin/trpt/trpt.8153
-rw-r--r--usr.sbin/trpt/trpt.c472
-rw-r--r--usr.sbin/tzsetup/Makefile11
-rw-r--r--usr.sbin/tzsetup/paths.h5
-rw-r--r--usr.sbin/tzsetup/tzsetup.8130
-rw-r--r--usr.sbin/tzsetup/tzsetup.c710
-rw-r--r--usr.sbin/ugidfw/Makefile9
-rw-r--r--usr.sbin/ugidfw/ugidfw.8210
-rw-r--r--usr.sbin/ugidfw/ugidfw.c208
-rw-r--r--usr.sbin/usbd/Makefile6
-rw-r--r--usr.sbin/usbd/usbd.8152
-rw-r--r--usr.sbin/usbd/usbd.c1116
-rw-r--r--usr.sbin/usbd/usbd.conf.5163
-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/Makefile6
-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.1533
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.c870
-rw-r--r--usr.sbin/vipw/Makefile12
-rw-r--r--usr.sbin/vipw/vipw.8111
-rw-r--r--usr.sbin/vipw/vipw.c141
-rw-r--r--usr.sbin/vnconfig/Makefile6
-rw-r--r--usr.sbin/vnconfig/vnconfig.c25
-rw-r--r--usr.sbin/watch/Makefile11
-rw-r--r--usr.sbin/watch/watch.8116
-rw-r--r--usr.sbin/watch/watch.c442
-rw-r--r--usr.sbin/watchdogd/Makefile14
-rw-r--r--usr.sbin/watchdogd/watchdog.870
-rw-r--r--usr.sbin/watchdogd/watchdogd.8126
-rw-r--r--usr.sbin/watchdogd/watchdogd.c277
-rw-r--r--usr.sbin/wicontrol/Makefile9
-rw-r--r--usr.sbin/wicontrol/wicontrol.8587
-rw-r--r--usr.sbin/wicontrol/wicontrol.c1239
-rw-r--r--usr.sbin/wlconfig/Makefile7
-rw-r--r--usr.sbin/wlconfig/wlconfig.8138
-rw-r--r--usr.sbin/wlconfig/wlconfig.c417
-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.c328
-rw-r--r--usr.sbin/ypbind/yp_ping.h5
-rw-r--r--usr.sbin/ypbind/ypbind.8205
-rw-r--r--usr.sbin/ypbind/ypbind.c991
-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.8181
-rw-r--r--usr.sbin/yppush/yppush_extern.h44
-rw-r--r--usr.sbin/yppush/yppush_main.c684
-rw-r--r--usr.sbin/ypserv/Makefile43
-rw-r--r--usr.sbin/ypserv/Makefile.yp577
-rw-r--r--usr.sbin/ypserv/yp_access.c323
-rw-r--r--usr.sbin/ypserv/yp_dblookup.c733
-rw-r--r--usr.sbin/ypserv/yp_dnslookup.c522
-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.c338
-rw-r--r--usr.sbin/ypserv/yp_server.c973
-rw-r--r--usr.sbin/ypserv/yp_svc_udp.c71
-rw-r--r--usr.sbin/ypserv/ypinit.8196
-rw-r--r--usr.sbin/ypserv/ypinit.sh386
-rw-r--r--usr.sbin/ypserv/ypserv.8447
-rw-r--r--usr.sbin/ypset/Makefile7
-rw-r--r--usr.sbin/ypset/ypset.888
-rw-r--r--usr.sbin/ypset/ypset.c151
-rw-r--r--usr.sbin/zic/Arts.htm3
-rw-r--r--usr.sbin/zic/Makefile367
-rw-r--r--usr.sbin/zic/Makefile.inc3
-rw-r--r--usr.sbin/zic/Music81
-rw-r--r--usr.sbin/zic/WWW71
-rw-r--r--usr.sbin/zic/ialloc.c7
-rw-r--r--usr.sbin/zic/private.h137
-rw-r--r--usr.sbin/zic/scheck.c7
-rw-r--r--usr.sbin/zic/zdump.865
-rw-r--r--usr.sbin/zic/zdump.c62
-rw-r--r--usr.sbin/zic/zdump/Makefile13
-rw-r--r--usr.sbin/zic/zic.8478
-rw-r--r--usr.sbin/zic/zic.c390
-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
1735 files changed, 459085 insertions, 1157 deletions
diff --git a/usr.sbin/IPXrouted/IPXrouted.8 b/usr.sbin/IPXrouted/IPXrouted.8
new file mode 100644
index 0000000..f9548fd
--- /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..312f13d
--- /dev/null
+++ b/usr.sbin/IPXrouted/af.c
@@ -0,0 +1,298 @@
+/*
+ * 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[] = "@(#)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..4e2af23
--- /dev/null
+++ b/usr.sbin/IPXrouted/main.c
@@ -0,0 +1,400 @@
+/*
+ * 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, 0); /* 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 fromlen = sizeof (from), cc, omask;
+ 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..fee3921
--- /dev/null
+++ b/usr.sbin/IPXrouted/tables.c
@@ -0,0 +1,435 @@
+/*
+ * 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(dst)
+ 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(dst)
+ 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(dst, gate, metric, ticks, state)
+ struct sockaddr *dst, *gate;
+ short metric, 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(ort, dst, gate, metric, ticks, state)
+ struct rt_entry *ort;
+ struct sockaddr *dst, *gate;
+ short metric, 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(rt, gate, metric, ticks)
+ struct rt_entry *rt;
+ struct sockaddr *gate;
+ short metric, 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(rt)
+ 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(action, ort)
+ 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..47a576d
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.c
@@ -0,0 +1,521 @@
+/*
+ * 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);
+
+void
+traceinit(ifp)
+ register struct interface *ifp;
+{
+ static int iftraceinit();
+
+ 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=%lx packet=%x\n", size,
+ (u_long)cp, (u_int)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=%lx packet=%x\n", size,
+ (u_long)cp, (u_int)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..732613a
--- /dev/null
+++ b/usr.sbin/Makefile
@@ -0,0 +1,330 @@
+# From: @(#)Makefile 5.20 (Berkeley) 6/12/93
+# $FreeBSD$
+
+# XXX MISSING: mkproto
+SUBDIR= ac \
+ accton \
+ ${_acpi} \
+ adduser \
+ amd \
+ ancontrol \
+ ${_apm} \
+ ${_apmd} \
+ ${_arlcontrol} \
+ arp \
+ ${_asf} \
+ ${_atm} \
+ ${_authpf} \
+ ${_bluetooth} \
+ ${_boot0cfg} \
+ ${_boot98cfg} \
+ bootparamd \
+ bsnmpd \
+ ${_btxld} \
+ burncd \
+ cdcontrol \
+ chkgrp \
+ chown \
+ chroot \
+ ckdist \
+ config \
+ cron \
+ crunch \
+ ctm \
+ daemon \
+ dconschat \
+ devinfo \
+ digictl \
+ diskinfo \
+ ${_editmap} \
+ edquota \
+ ${_eeprom} \
+ ${_elf2exe} \
+ extattr \
+ extattrctl \
+ faithd \
+ fdcontrol \
+ fdformat \
+ fdread \
+ fdwrite \
+ fwcontrol \
+ getfmac \
+ getpmac \
+ gstat \
+ ${_i4b} \
+ ifmcstat \
+ inetd \
+ iostat \
+ ip6addrctl \
+ ${_ipftest} \
+ ${_ipresend} \
+ ${_ipsend} \
+ ${_iptest} \
+ IPXrouted \
+ jail \
+ jexec \
+ jls \
+ kbdcontrol \
+ kbdmap \
+ kernbb \
+ ${_keyserv} \
+ ${_kgmon} \
+ ${_kgzip} \
+ kldxref \
+ lastlogin \
+ ${_lpr} \
+ ${_lptcontrol} \
+ ${_mailstats} \
+ mailwrapper \
+ ${_makemap} \
+ manctl \
+ memcontrol \
+ mergemaster \
+ mixer \
+ ${_mld6query} \
+ mlxcontrol \
+ mountd \
+ ${_mount_nwfs} \
+ mount_portalfs \
+ ${_mount_smbfs} \
+ moused \
+ ${_mptable} \
+ mrouted \
+ mtest \
+ mtree \
+ ${_named} \
+ ${_named.reload} \
+ ${_named.restart} \
+ ${_ndc} \
+ ${_ndiscvt} \
+ ndp \
+ newsyslog \
+ nfsd \
+ ngctl \
+ nghook \
+ nologin \
+ ${_nslookup} \
+ ${_nsupdate} \
+ ntp \
+ ${_ofwdump} \
+ ${_pccard} \
+ pciconf \
+ ${_pcvt} \
+ periodic \
+ pkg_install \
+ ${_pnpinfo} \
+ ppp \
+ ${_pppctl} \
+ pppd \
+ pppstats \
+ ${_praliases} \
+ procctl \
+ pstat \
+ pw \
+ pwd_mkdb \
+ quot \
+ quotaon \
+ rarpd \
+ raycontrol \
+ repquota \
+ ${_rip6query} \
+ rmt \
+ ${_route6d} \
+ rpcbind \
+ rpc.lockd \
+ rpc.statd \
+ rpc.umntall \
+ rpc.yppasswdd \
+ rpc.ypupdated \
+ rpc.ypxfrd \
+ rrenumd \
+ rtadvd \
+ rtprio \
+ rtsold \
+ rwhod \
+ sa \
+ ${_sendmail} \
+ setfmac \
+ setkey \
+ setpmac \
+ ${_sicontrol} \
+ sliplogin \
+ slstat \
+ smbmsg \
+ ${_spkrtest} \
+ spray \
+ ${_sysinstall} \
+ syslogd \
+ tcpdchk \
+ tcpdmatch \
+ tcpdump \
+ timed \
+ traceroute \
+ ${_traceroute6} \
+ trpt \
+ tzsetup \
+ ugidfw \
+ ${_usbd} \
+ ${_usbdevs} \
+ vidcontrol \
+ vipw \
+ vnconfig \
+ watch \
+ watchdogd \
+ wicontrol \
+ ${_wlconfig} \
+ ypbind \
+ yp_mkdb \
+ yppoll \
+ yppush \
+ ypserv \
+ ypset \
+ zic \
+ ${_zzz}
+
+.if ${MACHINE_ARCH} != "arm"
+_sysinstall= sysinstall
+.endif
+
+.if !defined(NOATM)
+_atm= atm
+.endif
+
+.if !defined(NO_BIND)
+_named= named
+_named.reload= named.reload
+_named.restart= named.restart
+_ndc= ndc
+_nslookup= nslookup
+_nsupdate= nsupdate
+.endif
+
+.if !defined(NOCRYPT) && !defined(NO_OPENSSL)
+_keyserv= keyserv
+.endif
+
+.if !defined(NOINET6)
+_mld6query= mld6query
+_rip6query= rip6query
+_route6d= route6d
+_traceroute6= traceroute6
+.endif
+
+.if !defined(NO_IPFILTER)
+_ipftest= ipftest
+_ipresend= ipresend
+_ipsend= ipsend
+_iptest= iptest
+.endif
+
+.if !defined(NO_PF) && !defined(NO_AUTHPF)
+_authpf= authpf
+.endif
+
+.if !defined(NO_LPR)
+_lpr= lpr
+.endif
+
+.if !defined(NO_SENDMAIL)
+_editmap= editmap
+_mailstats= mailstats
+_makemap= makemap
+_praliases= praliases
+_sendmail= sendmail
+.endif
+
+.if !defined(NO_USB)
+_usbd= usbd
+_usbdevs= usbdevs
+.endif
+
+.if ${MACHINE_ARCH} == "i386"
+.if !defined(NO_ACPI)
+_acpi= acpi
+.endif
+_apm= apm
+_apmd= apmd
+_asf= asf
+.if !defined(NO_BLUETOOTH)
+_bluetooth= bluetooth
+.endif
+.if ${MACHINE} == "i386"
+_arlcontrol= arlcontrol
+_boot0cfg= boot0cfg
+.endif
+.if ${MACHINE} == "pc98"
+_boot98cfg= boot98cfg
+.endif
+_btxld= btxld
+.if !defined(NO_I4B)
+_i4b= i4b
+.endif
+_kgmon= kgmon
+_kgzip= kgzip
+_lptcontrol= lptcontrol
+_mount_nwfs= mount_nwfs
+_mount_smbfs= mount_smbfs
+_mptable= mptable
+_ndiscvt= ndiscvt
+_pccard= pccard
+_pcvt= pcvt
+_pnpinfo= pnpinfo
+.if !defined(NOLIBPTHREAD)
+_pppctl= pppctl
+.endif
+_sicontrol= sicontrol
+_spkrtest= spkrtest
+_wlconfig= wlconfig
+_zzz= zzz
+.endif
+
+.if ${MACHINE_ARCH} == "alpha"
+_elf2exe= elf2exe
+_pnpinfo= pnpinfo
+.if !defined(NOLIBC_R)
+_pppctl= pppctl
+.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 !defined(NO_ACPI)
+_acpi= acpi
+.endif
+_asf= asf
+_boot0cfg= boot0cfg
+_btxld= btxld
+_kgmon= kgmon
+_lptcontrol= lptcontrol
+_mount_nwfs= mount_nwfs
+_mount_smbfs= mount_smbfs
+_mptable= mptable
+.if !defined(NOLIBPTHREAD)
+_pppctl= pppctl
+.endif
+_sicontrol= sicontrol
+_spkrtest= spkrtest
+_zzz= zzz
+.endif
+
+.if ${MACHINE_ARCH} == "ia64"
+.if !defined(NO_ACPI)
+_acpi= acpi
+.endif
+.if !defined(NOLIBPTHREAD)
+_pppctl= pppctl
+.endif
+_zzz= zzz
+.endif
+
+.if ${MACHINE_ARCH} == "sparc64"
+_eeprom= eeprom
+_ofwdump= ofwdump
+.if !defined(NOLIBC_R)
+_pppctl= pppctl
+.endif
+.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..4be5659
--- /dev/null
+++ b/usr.sbin/ac/ac.8
@@ -0,0 +1,153 @@
+.\"
+.\" 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 DIAGNOSTICS
+.Ex -std
+.Sh FILES
+.Bl -tag -width /var/log/wtmp -compact
+.It Pa /var/log/wtmp
+connect time accounting file
+.El
+.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..d2a1865
--- /dev/null
+++ b/usr.sbin/accton/accton.c
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#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..2fce920
--- /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 -I${ACPICA_DIR} -I${ACPICA_DIR}/compiler
+
+.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..5f862a2
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.2 2000/07/14 18:16:25 iwasaki Exp $
+# $FreeBSD$
+
+PROG= acpiconf
+MAN= acpiconf.8
+
+.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..1de3c7d
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/acpiconf.8
@@ -0,0 +1,96 @@
+.\"-
+.\" 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 January 12, 2001
+.Dt ACPICONF 8
+.Os
+.Sh NAME
+.Nm acpiconf
+.Nd control ACPI power management
+.Sh SYNOPSIS
+.Nm
+.Op Fl deh
+.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 d
+Disables ACPI power management.
+.It Fl e
+Enables ACPI power management.
+.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..5781b9e
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/acpiconf.c
@@ -0,0 +1,192 @@
+/*-
+ * 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"
+#define RC_SUSPEND_PATH "/etc/rc.suspend"
+#define RC_RESUME_PATH "/etc/rc.resume"
+
+static int acpifd;
+
+static int
+acpi_init()
+{
+ acpifd = open(ACPIDEV, O_RDWR);
+ if (acpifd == -1){
+ acpifd = open(ACPIDEV, O_RDONLY);
+ }
+ if (acpifd == -1){
+ err(EX_OSFILE, ACPIDEV);
+ }
+}
+
+static int
+acpi_enable_disable(int enable)
+{
+ if (ioctl(acpifd, enable, NULL) == -1) {
+ if (enable == ACPIIO_ENABLE)
+ err(EX_IOERR, "enable failed");
+ else
+ err(EX_IOERR, "disable failed");
+ }
+
+ return (0);
+}
+
+static int
+acpi_sleep(int sleep_type)
+{
+ char cmd[64];
+ int ret;
+
+ /* Run the suspend rc script, if available. */
+ if (access(RC_SUSPEND_PATH, X_OK) == 0) {
+ snprintf(cmd, sizeof(cmd), "%s acpi %d", RC_SUSPEND_PATH,
+ sleep_type);
+ system(cmd);
+ }
+
+ ret = ioctl(acpifd, ACPIIO_SETSLPSTATE, &sleep_type);
+
+ /* Run the resume rc script, if available. */
+ if (access(RC_RESUME_PATH, X_OK) == 0) {
+ snprintf(cmd, sizeof(cmd), "%s acpi %d", RC_RESUME_PATH,
+ sleep_type);
+ system(cmd);
+ }
+
+ if (ret != 0)
+ err(EX_IOERR, "sleep type (%d) failed", sleep_type);
+
+ return (0);
+}
+
+static int
+acpi_battinfo(int num)
+{
+ union acpi_battery_ioctl_arg battio;
+ const char *pwr_units;
+
+ if (num < 0 || num > 64)
+ err(EX_USAGE, "invalid battery %d", num);
+
+ battio.unit = num;
+ if (ioctl(acpifd, ACPIIO_CMBAT_GET_BIF, &battio) == -1)
+ err(EX_IOERR, "get battery info (%d) failed", num);
+ printf("Battery %d information\n", num);
+ if (battio.bif.units == 0)
+ pwr_units = "mWh";
+ else
+ pwr_units = "mAh";
+
+ printf("Design capacity:\t%d %s\n", battio.bif.dcap, pwr_units);
+ printf("Last full capacity:\t%d %s\n", battio.bif.lfcap, pwr_units);
+ printf("Technology:\t\t%s\n", battio.bif.btech == 0 ?
+ "primary (non-rechargeable)" : "secondary (rechargeable)");
+ printf("Design voltage:\t\t%d mV\n", battio.bif.dvol);
+ printf("Capacity (warn):\t%d %s\n", battio.bif.wcap, pwr_units);
+ printf("Capacity (low):\t\t%d %s\n", battio.bif.lcap, pwr_units);
+ printf("Low/warn granularity:\t%d %s\n", battio.bif.gra1, pwr_units);
+ printf("Warn/full granularity:\t%d %s\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);
+
+ return (0);
+}
+
+static void
+usage(const char* prog)
+{
+ printf("usage: %s [-deh] [-i batt] [-s 1-5]\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, "dehi:s:")) != -1) {
+ switch (c) {
+ case 'i':
+ acpi_battinfo(atoi(optarg));
+ break;
+ case 'd':
+ acpi_enable_disable(ACPIIO_DISABLE);
+ break;
+ case 'e':
+ acpi_enable_disable(ACPIIO_ENABLE);
+ break;
+ case 's':
+ if (optarg[0] == 'S')
+ sleep_type = optarg[1] - '0';
+ else
+ sleep_type = optarg[0] - '0';
+ if (sleep_type < 0 || sleep_type > 5)
+ errx(EX_USAGE, "invalid sleep type (%d)",
+ sleep_type);
+ break;
+ case 'h':
+ default:
+ usage(prog);
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (sleep_type != -1) {
+ sleep(1); /* wait 1 sec. for key-release event */
+ 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..c703224
--- /dev/null
+++ b/usr.sbin/acpi/acpidb/Makefile
@@ -0,0 +1,42 @@
+# $FreeBSD$
+
+PROG= acpidb
+SRCS+= acpidb.c
+SRCS+= osunixxf.c
+SRCS+= dbcmds.c dbdisply.c dbexec.c dbfileio.c \
+ dbhistry.c dbinput.c dbstats.c dbutils.c \
+ dbxface.c dmbuffer.c dmnames.c dmobject.c \
+ dmopcode.c dmresrc.c dmresrcl.c dmresrcs.c \
+ dmutils.c dmwalk.c dsfield.c dsinit.c \
+ dsmethod.c dsmthdat.c dsobject.c dsopcode.c \
+ dsutils.c dswexec.c dswload.c dswscope.c dswstate.c \
+ evevent.c evgpe.c evgpeblk.c evmisc.c \
+ evregion.c evrgnini.c evsci.c evxface.c \
+ evxfevnt.c evxfregn.c 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 \
+ hwacpi.c hwgpe.c hwregs.c hwsleep.c \
+ 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 \
+ psargs.c psopcode.c psparse.c psscope.c \
+ pstree.c psutils.c pswalk.c psxface.c \
+ rsaddr.c rscalc.c rscreate.c rsdump.c \
+ rsio.c rsirq.c rslist.c rsmemory.c \
+ rsmisc.c rsutils.c rsxface.c \
+ tbconvrt.c tbget.c tbgetall.c tbinstal.c \
+ tbrsdt.c tbutils.c tbxface.c tbxfroot.c \
+ utalloc.c utcopy.c utdebug.c utdelete.c \
+ uteval.c utglobal.c utinit.c utmath.c \
+ utmisc.c utobject.c utxface.c
+
+MAN= acpidb.8
+
+CFLAGS+= -DACPI_APPLICATION -DACPI_DEBUG_OUTPUT -DACPI_DEBUGGER \
+ -DACPI_DISASSEMBLER
+
+.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..72ad59c
--- /dev/null
+++ b/usr.sbin/acpi/acpidb/acpidb.c
@@ -0,0 +1,497 @@
+/*-
+ * 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/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 <acpi.h>
+#include <acnamesp.h>
+#include <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);
+
+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%x ", val);
+ printf(" / %u) >>", 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%x 0x%x\n",
+ rc->regtype, 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 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%x)]",
+ space_names[SpaceID], BitWidth, 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%x)]",
+ space_names[SpaceID], BitWidth, 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, \
+ void *HandlerContext, \
+ void *RegionContext) \
+{ \
+ 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 UINT32 DummyGlobalLock;
+
+static int
+load_dsdt(const char *dsdtfile)
+{
+ char filetmp[PATH_MAX];
+ u_int8_t *code, *amlptr;
+ struct stat sb;
+ int fd, fd2;
+ int error;
+ ACPI_TABLE_HEADER *tableptr;
+
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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);
+ }
+
+ AcpiGbl_FACS = malloc(sizeof (ACPI_COMMON_FACS));
+ if (AcpiGbl_FACS == NULL) {
+ fprintf(stderr, "could not allocate memory for FACS\n");
+ return (-1);
+ }
+ DummyGlobalLock = 0;
+ AcpiGbl_CommonFACS.GlobalLock = &DummyGlobalLock;
+ AcpiGbl_GlobalLockPresent = TRUE;
+
+ AcpiDbGetTableFromFile(filetmp, NULL);
+ AcpiUtSetIntegerWidth (AcpiGbl_DSDT->Revision);
+
+ 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;
+
+ 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..e0aa157
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpi.c
@@ -0,0 +1,825 @@
+/*-
+ * 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 FADTbody *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 FADTbody *fadt);
+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 FADTbody *fadt)
+{
+ struct ACPIsdt *dsdp;
+ struct FACSbody *facs;
+ int fadt_revision;
+
+ acpi_print_fadt(fadt);
+
+ 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=0x%08x\n", hpetp->base_addr);
+ 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_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 FADTbody *fadt)
+{
+ const char *pm;
+ char sep;
+
+ printf(BEGIN_COMMENT);
+ printf(" FADT:\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))
+ errx(1, "RSDT entry %d is corrupt", i);
+ if (!memcmp(sdp->signature, "FACP", 4))
+ acpi_handle_fadt((struct FADTbody *) sdp->body);
+ 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 {
+ 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);
+}
+
+void
+dsdt_save_file(char *outfile, 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(fd, dsdp, SIZEOF_SDT_HDR);
+ write(fd, dsdp->body, dsdp->len - SIZEOF_SDT_HDR);
+ close(fd);
+}
+
+void
+aml_disassemble(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;
+ }
+
+ /* Dump DSDT to the temp file */
+ write(fd, dsdp, SIZEOF_SDT_HDR);
+ write(fd, dsdp->body, dsdp->len - SIZEOF_SDT_HDR);
+ 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, 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 *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 (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..550420c
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpi_user.c
@@ -0,0 +1,212 @@
+/*-
+ * 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+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(__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 /* __i386__ */
+ return (NULL);
+}
+
+/*
+ * Public interfaces
+ */
+struct ACPIrsdp *
+acpi_find_rsd_ptr(void)
+{
+ struct ACPIrsdp *rsdp;
+ u_long addr;
+ size_t len;
+
+ acpi_user_init();
+
+ /* Attempt to use sysctl to find RSD PTR record. */
+ len = sizeof(addr);
+ if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) == 0) {
+ if ((rsdp = acpi_get_rsdp(addr)) != NULL)
+ return (rsdp);
+ else
+ warnx("sysctl %s does not point to RSDP",
+ machdep_acpi_root);
+ }
+
+ 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..f32e37d
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.8
@@ -0,0 +1,190 @@
+.\" 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 August 31, 2000
+.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
+a DSDT output file and optionally also disassemble it.
+.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 FADT
+.It HPET
+.It MADT
+.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 EXAMPLES
+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 BUGS
+In the current implementation,
+.Nm
+does not dump the
+Secondary System Descriptor Table (SSDT),
+Embedded Controller Descriptor Table (ECDT),
+or BOOT structures.
+.Sh FILES
+.Bl -tag -width /dev/mem
+.It Pa /dev/mem
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr mem 4 ,
+.Xr acpiconf 8 ,
+.Xr acpidb 8 ,
+.Xr iasl 8
+.Sh AUTHORS
+.An Doug Rabson Aq dfr@FreeBSD.org
+.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org
+.An Yasuo YOKOYAMA Aq yokoyama@jp.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 HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0
+and was rewritten to use
+.Xr iasl 8
+for
+.Fx 5.2 .
diff --git a/usr.sbin/acpi/acpidump/acpidump.c b/usr.sbin/acpi/acpidump/acpidump.c
new file mode 100644
index 0000000..269a595
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.c
@@ -0,0 +1,139 @@
+/*-
+ * 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);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char c, *progname;
+ char *dsdt_input_file, *dsdt_output_file;
+ struct ACPIsdt *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);
+ sdt = dsdt_load_file(dsdt_input_file);
+ } else {
+ if (vflag)
+ warnx("loading RSD PTR from /dev/mem");
+ sdt = 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(sdt);
+ }
+
+ /* Translate RSDT to DSDT pointer */
+ if (dsdt_input_file == NULL) {
+ sdt = sdt_from_rsdt(sdt, "FACP");
+ sdt = dsdt_from_fadt((struct FADTbody *)sdt->body);
+ }
+
+ /* Dump the DSDT to a file */
+ if (dsdt_output_file != NULL) {
+ if (vflag)
+ warnx("saving DSDT file: %s", dsdt_output_file);
+ dsdt_save_file(dsdt_output_file, sdt);
+ }
+
+ /* Disassemble the DSDT into ASL */
+ if (dflag) {
+ if (vflag)
+ warnx("disassembling DSDT, iasl messages follow");
+ aml_disassemble(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..1ebc56d
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.h
@@ -0,0 +1,337 @@
+/*-
+ * 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;
+ u_int32_t base_addr;
+ u_int64_t reserved1;
+ 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;
+
+/*
+ * 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 *);
+
+/* 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 *);
+
+/* 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 *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..afe0d81
--- /dev/null
+++ b/usr.sbin/acpi/iasl/Makefile
@@ -0,0 +1,48 @@
+# $FreeBSD$
+
+PROG= iasl
+SRCS= aslcompiler.y.h aslcompilerlex.l aslcompilerparse.y \
+ aslanalyze.c aslcodegen.c \
+ aslcompile.c aslerror.c aslfiles.c asllength.c \
+ asllisting.c aslload.c asllookup.c aslmain.c \
+ aslmap.c aslopcodes.c asloperands.c aslresource.c \
+ aslrestype1.c aslrestype2.c asltree.c aslutils.c \
+ asltransform.c aslfold.c aslstubs.c aslopt.c
+SRCS+= adisasm.c getopt.c osunixxf.c
+SRCS+= dbfileio.c dmbuffer.c dmnames.c dmopcode.c dmobject.c \
+ dmresrc.c dmresrcl.c dmresrcs.c dmutils.c dmwalk.c \
+ dsopcode.c dsutils.c dswexec.c dswload.c \
+ dswscope.c dswstate.c dsfield.c dsobject.c \
+ 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 \
+ nsaccess.c nsalloc.c nsdump.c nsnames.c nsobject.c \
+ nsparse.c nssearch.c nsutils.c nswalk.c nsxfobj.c \
+ psargs.c psopcode.c psparse.c psscope.c \
+ pstree.c psutils.c pswalk.c \
+ tbinstal.c tbutils.c \
+ utalloc.c utcopy.c utdebug.c utdelete.c \
+ utglobal.c utobject.c utmisc.c utmath.c
+
+MAN= iasl.8
+
+CFLAGS+= -D_ACPI_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..fa80aa7
--- /dev/null
+++ b/usr.sbin/acpi/iasl/iasl.8
@@ -0,0 +1,174 @@
+.\"-
+.\" 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 IASL 8
+.Os
+.Sh NAME
+.Nm iasl
+.Nd Intel ACPI compiler/decompiler
+.Sh SYNOPSIS
+.Nm
+.Op Fl cefghl
+.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
+.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 2.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 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.
+.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..a87e6af
--- /dev/null
+++ b/usr.sbin/adduser/adduser.8
@@ -0,0 +1,473 @@
+.\" 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 30, 2004
+.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 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.
+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 and 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
+Normaly,
+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
+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 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 p
+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 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 writeable
+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 setable
+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..c00666d
--- /dev/null
+++ b/usr.sbin/adduser/adduser.conf.5
@@ -0,0 +1,204 @@
+.\"
+.\" 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 March 30, 2004
+.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 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 uuid
+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 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..7e21cb5
--- /dev/null
+++ b/usr.sbin/adduser/adduser.sh
@@ -0,0 +1,1003 @@
+#!/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 " -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
+
+ 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
+
+ # /usr/sbin/nologin is a special case
+ if [ "$_shell" = "${NOLOGIN}" ]; then
+ echo ${NOLOGIN_PATH}
+ return 0;
+ fi
+
+ 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 "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}
+}
+
+# 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
+ _home='-m -d "$uhome"'
+ 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. 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
+ 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_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=
+
+ # No need to take down uids for a configuration saving run.
+ [ -n "$configflag" ] && return
+
+ 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
+ \#*|'')
+ return 0
+ ;;
+ esac
+
+ get_user || continue
+ get_gecos
+ get_uid
+ get_logingroup
+ get_class
+ get_shell
+ get_homedir
+ get_password
+ get_expire_dates
+
+ add_user
+ 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
+
+ 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" "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 DEFENITION ####
+
+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=
+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}"
+
+# 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
+ ;;
+ -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..913cb5d
--- /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 writeable 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 inlcudes 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..004da08
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.sh
@@ -0,0 +1,360 @@
+#!/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`
+
+# 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:"
+ pw 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 user name's: "
+ 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
+ pw 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..dd65ed3
--- /dev/null
+++ b/usr.sbin/amd/Makefile.inc
@@ -0,0 +1,36 @@
+# 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 $
+
+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
+
+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..eb30395
--- /dev/null
+++ b/usr.sbin/amd/amd/Makefile
@@ -0,0 +1,42 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+#
+
+.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
+SRCS+= amfs_host.c amfs_inherit.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_file.c info_hesiod.c info_ndbm.c info_nis.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 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}
+LDADD= ${LIBAMU}
+
+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}
+
+.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..9ae55ad
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/Makefile
@@ -0,0 +1,22 @@
+# 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
+
+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..d3cf000
--- /dev/null
+++ b/usr.sbin/amd/include/Makefile
@@ -0,0 +1,19 @@
+# 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 $
+
+SRCS= config_local.h
+CLEANFILES= ${SRCS}
+
+all depend: ${SRCS}
+
+config_local.h: newvers.sh
+ @rm -f ${.TARGET}
+ sh ${.ALLSRC} ${.CURDIR}/../../../sys/conf/newvers.sh > ${.TARGET}
+
+.include <bsd.prog.mk>
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..a26d3cd
--- /dev/null
+++ b/usr.sbin/amd/include/aux_conf.h
@@ -0,0 +1,81 @@
+/* $FreeBSD$ */
+
+/*
+ * aux_conf.h:
+ * This file gets "filled in" for each architecture.
+ * Generated automatically 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/aux/macros.
+ */
+
+/* $srcdir/conf/trap/trap_default.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) mount(type, mnt->mnt_dir, flags, mnt_data)
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/trap/trap_default.h"
+/* 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/aux/macros.
+ */
+#define UNMOUNT_TRAP(mnt) unmount(mnt->mnt_dir)
+/* End of replaced UNMOUNT_TRAP macro definition */
+
+/*
+ * 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/aux/macros.
+ */
+
+/* $srcdir/conf/fh_dref/fh_dref_freebsd22.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define NFS_FH_DREF(dst, src) (dst) = (u_char *) (src)
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/fh_dref/fh_dref_freebsd22.h"
+/* 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/aux/macros.
+ */
+
+/* $srcdir/conf/sa_dref/sa_dref_bsd44.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define NFS_SA_DREF(dst, src) { \
+ (dst)->addr = (struct sockaddr *) (src); \
+ (dst)->addrlen = sizeof(*src); \
+ }
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/sa_dref/sa_dref_bsd44.h"
+/* 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/aux/macros.
+ */
+
+/* $srcdir/conf/hn_dref/hn_dref_default.h */
+#ifdef COMMENT_OUT__GET_DIRECTLY_FROM_FILE
+#define NFS_HN_DREF(dst, src) (dst) = (src)
+#endif /* COMMENT_OUT__GET_DIRECTLY_FROM_FILE */
+#include "conf/hn_dref/hn_dref_default.h"
+/* 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..2f80eb2
--- /dev/null
+++ b/usr.sbin/amd/include/build_version.h
@@ -0,0 +1,6 @@
+/* do not edit this file by hand */
+/* auto-generated by update_build_version script */
+/* #define AMU_BUILD_VERSION 1 */
+
+#include <sys/param.h>
+#define AMU_BUILD_VERSION __FreeBSD_version
diff --git a/usr.sbin/amd/include/config.h b/usr.sbin/amd/include/config.h
new file mode 100644
index 0000000..ddfeee0
--- /dev/null
+++ b/usr.sbin/amd/include/config.h
@@ -0,0 +1,2037 @@
+/*
+ * $FreeBSD$
+ *
+ * portions derived from
+ * $NetBSD: config.h,v 1.11 1998/08/08 22:33:37 christos Exp $
+ *
+ */
+
+/*
+ * Start of am-utils-6.x config.h file.
+ * Erez Zadok <ezk @ cs.sunysb.edu>
+ *
+ * DO NOT EDIT BY HAND.
+ * Note: generated by autoheader from configure.in.
+ */
+
+#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 Sep 2 16:41:46 CEST 2003" */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Turn off general debugging by default */
+/* #undef DEBUG */
+
+/* Turn off memory debugging by default */
+/* #undef DEBUG_MEM */
+
+/* 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 to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+/* #undef HAVE_ALLOCA_H */
+
+/* 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 inheritance filesystem */
+#define HAVE_AMU_FS_INHERIT 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 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
+
+/* Define if `flags' is member of `cdfs_args_t'. */
+#define HAVE_CDFS_ARGS_T_FLAGS 1
+
+/* Define if `fspec' is member of `cdfs_args_t'. */
+#define HAVE_CDFS_ARGS_T_FSPEC 1
+
+/* Define if `iso_flags' is member of `cdfs_args_t'. */
+/* #undef HAVE_CDFS_ARGS_T_ISO_FLAGS */
+
+/* Define if `iso_pgthresh' is member of `cdfs_args_t'. */
+/* #undef HAVE_CDFS_ARGS_T_ISO_PGTHRESH */
+
+/* Define if `norrip' is member of `cdfs_args_t'. */
+/* #undef HAVE_CDFS_ARGS_T_NORRIP */
+
+/* Define 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_timed' function. */
+#define HAVE_CLNT_CREATE_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 if `flags' is member of `efs_args_t'. */
+/* #undef HAVE_EFS_ARGS_T_FLAGS */
+
+/* Define 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 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 get_myaddress() exist? */
+#define HAVE_EXTERN_GET_MYADDRESS 1
+
+/* does extern definition for innetgr() exist? */
+#define HAVE_EXTERN_INNETGR 1
+
+/* does extern definition for mkstemp() exist? */
+#define HAVE_EXTERN_MKSTEMP 1
+
+/* 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 strcasecmp() exist? */
+#define HAVE_EXTERN_STRCASECMP 1
+
+/* does extern definition for strdup() exist? */
+#define HAVE_EXTERN_STRDUP 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 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 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 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 */
+/* #undef HAVE_FS_NULLFS */
+
+/* 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 */
+
+/* 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 */
+
+/* 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
+
+/* Define to 1 if you have the <hsfs/hsfs.h> header file. */
+/* #undef HAVE_HSFS_HSFS_H */
+
+/* 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 `gdbm' library (-lgdbm). */
+/* #undef HAVE_LIBGDBM */
+
+/* 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 `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 */
+
+/* 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/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 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 <malloc.h> header file. */
+/* #undef HAVE_MALLOC_H */
+
+/* Define if have DBM maps */
+/* #undef HAVE_MAP_DBM */
+
+/* Define if have file maps (everyone should have it!) */
+#define HAVE_MAP_FILE 1
+
+/* Define if have HESIOD maps */
+#define HAVE_MAP_HESIOD 1
+
+/* 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 if `mnt_cnode' is member of `mntent_t'. */
+/* #undef HAVE_MNTENT_T_MNT_CNODE */
+
+/* Define if `mnt_ro' is member of `mntent_t'. */
+/* #undef HAVE_MNTENT_T_MNT_RO */
+
+/* Define 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 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 <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 if `acdirmin' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_ACDIRMIN 1
+
+/* Define if `acregmin' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_ACREGMIN 1
+
+/* Define if `bsize' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_BSIZE */
+
+/* Define if `fhsize' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_FHSIZE 1
+
+/* Define if `fh_len' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_FH_LEN */
+
+/* Define if `gfs_flags' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_GFS_FLAGS */
+
+/* Define if `namlen' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_NAMLEN */
+
+/* Define if `optstr' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_OPTSTR */
+
+/* Define if `proto' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_PROTO 1
+
+/* Define if `sotype' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_SOTYPE 1
+
+/* Define 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 if `dsttime' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_DSTTIME */
+
+/* Define if `fspec' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_FSPEC */
+
+/* Define if `gid' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_GID */
+
+/* Define if `mask' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_MASK */
+
+/* Define if `secondswest' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_SECONDSWEST */
+
+/* Define if `uid' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_UID */
+
+/* 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/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 `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 <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 `strspn' function. */
+#define HAVE_STRSPN 1
+
+/* Define to 1 if you have the `strstr' function. */
+#define HAVE_STRSTR 1
+
+/* Define if `fhs_fh' is member of `struct fhstatus'. */
+/* #undef HAVE_STRUCT_FHSTATUS_FHS_FH */
+
+/* Define if `ifa_next' is member of `struct ifaddrs'. */
+#define HAVE_STRUCT_IFADDRS_IFA_NEXT 1
+
+/* Define 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 if `sa_len' is member of `struct sockaddr'. */
+#define HAVE_STRUCT_SOCKADDR_SA_LEN 1
+
+/* Define if `f_fstypename' is member of `struct statfs'. */
+#define HAVE_STRUCT_STATFS_F_FSTYPENAME 1
+
+/* 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/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/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/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 <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 if `flags' is member of `ufs_args_t'. */
+/* #undef HAVE_UFS_ARGS_T_FLAGS */
+
+/* Define if `fspec' is member of `ufs_args_t'. */
+#define HAVE_UFS_ARGS_T_FSPEC 1
+
+/* Define if `ufs_flags' is member of `ufs_args_t'. */
+/* #undef HAVE_UFS_ARGS_T_UFS_FLAGS */
+
+/* Define 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_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 `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. */
+#define HAVE_VARARGS_H 1
+
+/* 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_mountres3' function. */
+/* #undef HAVE_XDR_MOUNTRES3 */
+
+/* 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 if `flags' is member of `xfs_args_t'. */
+/* #undef HAVE_XFS_ARGS_T_FLAGS */
+
+/* Define 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 `__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) */
+/* #define HOST_HEADER_VERSION "5.1" */
+
+/* Define name and version of host machine (eg. solaris2.5.1) */
+/* #define HOST_OS "freebsd5.1" */
+
+/* Define only name of host machine OS (eg. solaris2) */
+/* #define HOST_OS_NAME "freebsd5" */
+
+/* Define only version of host machine (eg. 2.5.1) */
+/* #define HOST_OS_VERSION "5.1" */
+
+/* Define name of host machine's vendor (eg. sun) */
+#define HOST_VENDOR "unknown"
+
+/* 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 */
+
+/* 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 0x10
+
+/* 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 */
+
+/* Use Readdirplus for NFSv3 */
+/* #undef MNT2_NFS_OPTS_RDIRPLUS */
+
+/* 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 */
+
+/* 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 */
+
+/* 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 */
+
+/* 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 */
+
+/* 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 */
+
+/* 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: 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 */
+
+/* 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 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 */
+/* #undef MNTTAB_TYPE_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 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 */
+/* #undef MOUNT_TYPE_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 *
+
+/* 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 "amd-dev@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.0-20030828"
+
+/* 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.0-20030828"
+
+/* 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 */
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* 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 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.0-20030828"
+
+/* 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 as `__inline' if that's what the C compiler calls it, or to nothing
+ if it is not supported. */
+/* #undef inline */
+
+/* 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 */
+/* #undef pcfs_args_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define a type for the rfs_args structure */
+/* #undef rfs_args_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..bfdbfc8
--- /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 HOST_NAME "`hostname`"
+
+/* Define user name */
+#define USER_NAME "`whoami`"
+
+/* Define configuration date */
+#define CONFIG_DATE "`date`"
+
+__EOF
diff --git a/usr.sbin/amd/libamu/Makefile b/usr.sbin/amd/libamu/Makefile
new file mode 100644
index 0000000..8a5de08
--- /dev/null
+++ b/usr.sbin/amd/libamu/Makefile
@@ -0,0 +1,31 @@
+# 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= YES
+SRCS= hasmntopt.c misc_rpc.c mount_fs.c mtab.c nfs_prot_xdr.c \
+ util.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
+CLEANFILES+= nfs_prot_x.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}
+
+.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..ff792bd
--- /dev/null
+++ b/usr.sbin/amd/mk-amd-map/Makefile
@@ -0,0 +1,13 @@
+# 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
+
+.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..8181a08
--- /dev/null
+++ b/usr.sbin/ancontrol/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= ancontrol
+MAN= ancontrol.8
+
+WARNS?= 2
+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..f9c6797
--- /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 isn't 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 isn't 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 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.
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Bill Paul Aq wpaul@ee.columbia.edu .
diff --git a/usr.sbin/ancontrol/ancontrol.c b/usr.sbin/ancontrol/ancontrol.c
new file mode 100644
index 0000000..1d5d86a
--- /dev/null
+++ b/usr.sbin/ancontrol/ancontrol.c
@@ -0,0 +1,1833 @@
+/*
+ * 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 <sys/socket.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(u_int16_t *, int);
+static void an_printspeeds(u_int8_t*, int);
+static void an_printbool(int);
+static void an_printhex(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(char *, struct an_ltv_key *);
+static void an_setkeys(const char *, char *, int);
+static void an_enable_tx_key(const char *, char *);
+static void an_enable_leap_mode(const char *, char *);
+static void an_dumprssimap(const char *);
+static void usage(char *);
+int main(int, 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(iface, areq)
+ const char *iface;
+ struct an_req *areq;
+{
+ struct ifreq ifr;
+ int s, okay = 1;
+
+ bzero((char *)&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(iface, areq)
+ const char *iface;
+ struct an_req *areq;
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&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(str, len)
+ char *str;
+ int len;
+{
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ if (str[i] == '\0')
+ str[i] = ' ';
+ }
+
+ printf("[ %.*s ]", len, str);
+
+ return;
+}
+
+static void an_printwords(w, len)
+ u_int16_t *w;
+ int len;
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len; i++)
+ printf("%d ", w[i]);
+ printf("]");
+
+ return;
+}
+
+static void an_printspeeds(w, len)
+ 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("]");
+
+ return;
+}
+
+static void an_printbool(val)
+ int val;
+{
+ if (val)
+ printf("[ On ]");
+ else
+ printf("[ Off ]");
+
+ return;
+}
+
+static void an_printhex(ptr, len)
+ char *ptr;
+ int len;
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len; i++) {
+ printf("%02x", ptr[i] & 0xFF);
+ if (i < (len - 1))
+ printf(":");
+ }
+
+ printf(" ]");
+ return;
+}
+
+
+
+static void an_dumpstatus(iface)
+ 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[ %d%% ]",
+ an_rssimap.an_entries[
+ sts->an_normalized_strength].an_rss_pct);
+ else
+ printf("\nSignal strength:\t[ %d%% ]",
+ sts->an_normalized_strength);
+ printf("\nAverage Noise:\t\t[ %d%% ]",sts->an_avg_noise_prev_min_pc);
+ if (rssimap_valid)
+ printf("\nSignal quality:\t\t[ %d%% ]",
+ an_rssimap.an_entries[
+ sts->an_cur_signal_quality].an_rss_pct);
+ else
+ printf("\nSignal quality:\t\t[ %d ]",
+ sts->an_cur_signal_quality);
+ printf("\nMax Noise:\t\t[ %d%% ]",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[ %d%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(iface)
+ 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(iface)
+ const char *iface;
+{
+ struct an_ltv_stats *stats;
+ struct an_req areq;
+ caddr_t ptr;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_32BITS_CUM;
+
+ an_getval(iface, &areq);
+
+ ptr = (caddr_t)&areq;
+ ptr -= 2;
+ stats = (struct an_ltv_stats *)ptr;
+
+ printf("RX overruns:\t\t\t\t\t[ %d ]\n", stats->an_rx_overruns);
+ printf("RX PLCP CSUM errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_plcp_csum_errs);
+ printf("RX PLCP format errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_plcp_format_errs);
+ printf("RX PLCP length errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_plcp_len_errs);
+ printf("RX MAC CRC errors:\t\t\t\t[ %d ]\n",
+ stats->an_rx_mac_crc_errs);
+ printf("RX MAC CRC OK:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_mac_crc_ok);
+ printf("RX WEP errors:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_wep_errs);
+ printf("RX WEP OK:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_wep_ok);
+ printf("Long retries:\t\t\t\t\t[ %d ]\n",
+ stats->an_retry_long);
+ printf("Short retries:\t\t\t\t\t[ %d ]\n",
+ stats->an_retry_short);
+ printf("Retries exhausted:\t\t\t\t[ %d ]\n",
+ stats->an_retry_max);
+ printf("Bad ACK:\t\t\t\t\t[ %d ]\n",
+ stats->an_no_ack);
+ printf("Bad CTS:\t\t\t\t\t[ %d ]\n",
+ stats->an_no_cts);
+ printf("RX good ACKs:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_ack_ok);
+ printf("RX good CTSs:\t\t\t\t\t[ %d ]\n",
+ stats->an_rx_cts_ok);
+ printf("TX good ACKs:\t\t\t\t\t[ %d ]\n",
+ stats->an_tx_ack_ok);
+ printf("TX good RTSs:\t\t\t\t\t[ %d ]\n",
+ stats->an_tx_rts_ok);
+ printf("TX good CTSs:\t\t\t\t\t[ %d ]\n",
+ stats->an_tx_cts_ok);
+ printf("LMAC multicasts transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_mcasts);
+ printf("LMAC broadcasts transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_bcasts);
+ printf("LMAC unicast frags transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_ucast_frags);
+ printf("LMAC unicasts transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_lmac_ucasts);
+ printf("Beacons transmitted:\t\t\t\t[ %d ]\n",
+ stats->an_tx_beacons);
+ printf("Beacons received:\t\t\t\t[ %d ]\n",
+ stats->an_rx_beacons);
+ printf("Single transmit collisions:\t\t\t[ %d ]\n",
+ stats->an_tx_single_cols);
+ printf("Multiple transmit collisions:\t\t\t[ %d ]\n",
+ stats->an_tx_multi_cols);
+ printf("Transmits without deferrals:\t\t\t[ %d ]\n",
+ stats->an_tx_defers_no);
+ printf("Transmits deferred due to protocol:\t\t[ %d ]\n",
+ stats->an_tx_defers_prot);
+ printf("Transmits deferred due to energy detect:\t\t[ %d ]\n",
+ stats->an_tx_defers_energy);
+ printf("RX duplicate frames/frags:\t\t\t[ %d ]\n",
+ stats->an_rx_dups);
+ printf("RX partial frames:\t\t\t\t[ %d ]\n",
+ stats->an_rx_partial);
+ printf("TX max lifetime exceeded:\t\t\t[ %d ]\n",
+ stats->an_tx_too_old);
+ printf("RX max lifetime exceeded:\t\t\t[ %d ]\n",
+ stats->an_tx_too_old);
+ printf("Sync lost due to too many missed beacons:\t[ %d ]\n",
+ stats->an_lostsync_missed_beacons);
+ printf("Sync lost due to ARL exceeded:\t\t\t[ %d ]\n",
+ stats->an_lostsync_arl_exceeded);
+ printf("Sync lost due to deauthentication:\t\t[ %d ]\n",
+ stats->an_lostsync_deauthed);
+ printf("Sync lost due to disassociation:\t\t[ %d ]\n",
+ stats->an_lostsync_disassociated);
+ printf("Sync lost due to excess change in TSF timing:\t[ %d ]\n",
+ stats->an_lostsync_tsf_timing);
+ printf("Host transmitted multicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_host_mcasts);
+ printf("Host transmitted broadcasts:\t\t\t[ %d ]\n",
+ stats->an_tx_host_bcasts);
+ printf("Host transmitted unicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_host_ucasts);
+ printf("Host transmission failures:\t\t\t[ %d ]\n",
+ stats->an_tx_host_failed);
+ printf("Host received multicasts:\t\t\t[ %d ]\n",
+ stats->an_rx_host_mcasts);
+ printf("Host received broadcasts:\t\t\t[ %d ]\n",
+ stats->an_rx_host_bcasts);
+ printf("Host received unicasts:\t\t\t\t[ %d ]\n",
+ stats->an_rx_host_ucasts);
+ printf("Host receive discards:\t\t\t\t[ %d ]\n",
+ stats->an_rx_host_discarded);
+ printf("HMAC transmitted multicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_mcasts);
+ printf("HMAC transmitted broadcasts:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_bcasts);
+ printf("HMAC transmitted unicasts:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_ucasts);
+ printf("HMAC transmissions failed:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_failed);
+ printf("HMAC received multicasts:\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_mcasts);
+ printf("HMAC received broadcasts:\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_bcasts);
+ printf("HMAC received unicasts:\t\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_ucasts);
+ printf("HMAC receive discards:\t\t\t\t[ %d ]\n",
+ stats->an_rx_hmac_discarded);
+ printf("HMAC transmits accepted:\t\t\t[ %d ]\n",
+ stats->an_tx_hmac_accepted);
+ printf("SSID mismatches:\t\t\t\t[ %d ]\n",
+ stats->an_ssid_mismatches);
+ printf("Access point mismatches:\t\t\t[ %d ]\n",
+ stats->an_ap_mismatches);
+ printf("Speed mismatches:\t\t\t\t[ %d ]\n",
+ stats->an_rates_mismatches);
+ printf("Authentication rejects:\t\t\t\t[ %d ]\n",
+ stats->an_auth_rejects);
+ printf("Authentication timeouts:\t\t\t[ %d ]\n",
+ stats->an_auth_timeouts);
+ printf("Association rejects:\t\t\t\t[ %d ]\n",
+ stats->an_assoc_rejects);
+ printf("Association timeouts:\t\t\t\t[ %d ]\n",
+ stats->an_assoc_timeouts);
+ printf("Management frames received:\t\t\t[ %d ]\n",
+ stats->an_rx_mgmt_pkts);
+ printf("Management frames transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_mgmt_pkts);
+ printf("Refresh frames received:\t\t\t[ %d ]\n",
+ stats->an_rx_refresh_pkts),
+ printf("Refresh frames transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_refresh_pkts),
+ printf("Poll frames received:\t\t\t\t[ %d ]\n",
+ stats->an_rx_poll_pkts);
+ printf("Poll frames transmitted:\t\t\t[ %d ]\n",
+ stats->an_tx_poll_pkts);
+ printf("Host requested sync losses:\t\t\t[ %d ]\n",
+ stats->an_lostsync_hostreq);
+ printf("Host transmitted bytes:\t\t\t\t[ %d ]\n",
+ stats->an_host_tx_bytes);
+ printf("Host received bytes:\t\t\t\t[ %d ]\n",
+ stats->an_host_rx_bytes);
+ printf("Uptime in microseconds:\t\t\t\t[ %d ]\n",
+ stats->an_uptime_usecs);
+ printf("Uptime in seconds:\t\t\t\t[ %d ]\n",
+ stats->an_uptime_secs);
+ printf("Sync lost due to better AP:\t\t\t[ %d ]\n",
+ stats->an_lostsync_better_ap);
+
+ return;
+}
+
+static void an_dumpap(iface)
+ 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(iface)
+ 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("To 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(iface)
+ 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);
+
+ return;
+}
+
+static void an_dumprssimap(iface)
+ 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);
+ }
+
+ return;
+}
+
+static void usage(p)
+ 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(iface, act, arg)
+ 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: %d", 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((char *)addr, (char *)&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(iface, act, arg)
+ 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(iface, act, arg)
+ 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((char *)addr, (char *)&ap->an_ap1, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP2:
+ bzero(ap->an_ap2, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&ap->an_ap2, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP3:
+ bzero(ap->an_ap3, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&ap->an_ap3, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP4:
+ bzero(ap->an_ap4, ETHER_ADDR_LEN);
+ bcopy((char *)addr, (char *)&ap->an_ap4, ETHER_ADDR_LEN);
+ break;
+ default:
+ errx(1, "unknown action");
+ break;
+ }
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+static void an_setssid(iface, act, arg)
+ 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("To 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(iface)
+ const char *iface;
+{
+ struct an_req areq;
+
+ bzero((char *)&areq, sizeof(areq));
+ areq.an_len = 0;
+ areq.an_type = AN_RID_ZERO_CACHE;
+
+ an_getval(iface, &areq);
+
+ return;
+}
+
+static void an_readcache(iface)
+ const char *iface;
+{
+ struct an_req areq;
+ int *an_sigitems;
+ struct an_sigcache *sc;
+ char * pt;
+ int i;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&areq, sizeof(areq));
+ areq.an_len = AN_MAX_DATALEN;
+ areq.an_type = AN_RID_READ_CACHE;
+
+ an_getval(iface, &areq);
+
+ an_sigitems = (int *) &areq.an_val;
+ pt = ((char *) &areq.an_val);
+ pt += sizeof(int);
+ sc = (struct an_sigcache *) pt;
+
+ 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++;
+ }
+
+ return;
+}
+#endif
+
+static int an_hex2int(c)
+ 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(s, k)
+ 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(iface, key, keytype)
+ const char *iface;
+ char *key;
+ int keytype;
+{
+ struct an_req areq;
+ struct an_ltv_key *k;
+
+ bzero((char *)&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;
+ }
+
+ return;
+}
+
+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((char *)&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 %d is unset\n",k->kindex);
+ break;
+ case 5:
+ printf("\tKey %d is set 40 bits\n",k->kindex);
+ break;
+ case 13:
+ printf("\tKey %d is set 128 bits\n",k->kindex);
+ break;
+ default:
+ printf("\tWEP Key %d has an unknown size %d\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(iface, arg)
+ const char *iface;
+ char *arg;
+{
+ struct an_req areq;
+ struct an_ltv_key *k;
+ struct an_ltv_genconfig *config;
+
+ bzero((char *)&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((char *)&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);
+
+ return;
+}
+
+static void an_enable_leap_mode(iface, username)
+ const char *iface;
+ 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(argc, argv)
+ 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: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..6067bee
--- /dev/null
+++ b/usr.sbin/apm/apm.8
@@ -0,0 +1,148 @@
+.\" 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 and 1 correspond to the
+.Dq off-line
+state or
+.Dq on-line
+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
+doesn't 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 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 .
+.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
diff --git a/usr.sbin/apm/apm.c b/usr.sbin/apm/apm.c
new file mode 100644
index 0000000..c652ec1
--- /dev/null
+++ b/usr.sbin/apm/apm.c
@@ -0,0 +1,505 @@
+/*
+ * 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 <sys/file.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#define APMDEV "/dev/apm"
+
+#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 >= 255)
+ 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 >= 255)
+ 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" };
+
+ 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 >= 255)
+ printf("unknown\n");
+ else if (aip->ai_acline > 1)
+ 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 >= 255)
+ 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);
+ printf("\tBattery status: ");
+ if (aps.ap_batt_flag <= 255 &&
+ (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) {
+ printf("APM Capabilities:\n");
+ if (aip->ai_capabilities == 0xff00)
+ printf("\tunknown\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..ae491d8
--- /dev/null
+++ b/usr.sbin/apmd/apmd.8
@@ -0,0 +1,314 @@
+.\" 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 doesn't 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 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 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 SEE ALSO
+.Xr apm 4 ,
+.Xr apm 8
+.Sh AUTHORS
+.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org
+.An KOIE Hidetaka Aq koie@suri.co.jp
+.Pp
+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 .
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 3.3 .
diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c
new file mode 100644
index 0000000..79da664
--- /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(POWERSTATECHANG, 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")) != EOF) {
+ 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..1562c55f
--- /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:")) != EOF) {
+ 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/arlcontrol/Makefile b/usr.sbin/arlcontrol/Makefile
new file mode 100644
index 0000000..a7b4a14
--- /dev/null
+++ b/usr.sbin/arlcontrol/Makefile
@@ -0,0 +1,11 @@
+#
+# $RISS: if_arl/arlconfig/Makefile,v 1.5 2003/01/13 08:05:29 frol Exp $
+# $FreeBSD$
+#
+
+PROG= arlcontrol
+SRCS= arlcontrol.c
+CFLAGS= -I${.CURDIR}/../../sys -DARLCACHE
+MAN= arlcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/arlcontrol/arlcontrol.8 b/usr.sbin/arlcontrol/arlcontrol.8
new file mode 100644
index 0000000..54698a3
--- /dev/null
+++ b/usr.sbin/arlcontrol/arlcontrol.8
@@ -0,0 +1,166 @@
+.\" Copyright (c) 2004
+.\" <ran@styx.aic.net>. All right reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided 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 <ran@styx.aic.net>
+.\" 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 ran@styx.aic.net AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL <ran@styx.aic.net> 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 15, 2004
+.Dt ARLCONTROL 8 i386
+.Os
+.Sh NAME
+.Nm arlcontrol
+.Nd configure Aironet Arlan 655 device
+.Sh SYNOPSIS
+.Nm
+.Ar iface
+.Nm
+.Ar iface Cm country Ar country Pq Cm 9 Ns - Ns Cm 15
+.Nm
+.Ar iface Cm priority Ar priority Pq Cm normal , high , highest
+.Nm
+.Ar iface Cm txretry Ar txretry
+.Nm
+.Ar iface Cm stat
+.Nm
+.Ar iface Cm quality
+.Sh DESCRIPTION
+The
+.Nm
+utility controls the operation of Aironet Arlan 655 wireless networking
+devices via the
+.Xr arl 4
+driver.
+Most of the parameters that can be changed are related to the
+Aironet protocol which the Aironet Arlan 655 card implements.
+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 Arlan 655
+device
+.Li ( arl0 , arl1 ,
+etc.) and must be specified.
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Ar iface
+Display current settings of the specified wireless interface.
+This retrieves current card settings from the driver and prints
+them out.
+.It Ar iface Cm country Ar country Pq Cm 9 Ns - Ns Cm 15
+Chose
+.Ar country , channel
+parameters that depend on the
+.Ar country
+according to the following table:
+.Bl -column ".No North America" ".Em Channel" ".Em MHz"
+.Em "Country Ch_Set MHz"
+.It "North America" Ta 9 Ta "2412, 2427, 2442, 2457, 2465"
+.It "U.S.A" Ta 9 Ta "2412, 2427, 2442, 2457, 2465"
+.It "Canada" Ta 9 Ta "2412, 2427, 2442, 2457, 2465"
+.It "Mexico" Ta 9 Ta "2412, 2427, 2442, 2457, 2465"
+.It "New Zealand" Ta 9 Ta "2412, 2427, 2442, 2457, 2465"
+.It "E.T.S.I" Ta 10 Ta "2412, 2427, 2442, 2457, 2472"
+.It "Japan" Ta 11 Ta "2484"
+.It "France" Ta 12 Ta "2457, 2465, 2475"
+.It "Australia" Ta 13 Ta "2411, 2425, 2439"
+.It "Germany" Ta 14 Ta "2427, 2442, 2457"
+.It "U.K.(MPT1349)" Ta 15 Ta "2460"
+.It "U.K." Ta 15 Ta "2460"
+.It "Spain" Ta 15 Ta "2460"
+.El
+.It Ar iface Cm priority Ar priority Pq Cm normal , high , highest
+Use the
+.Ar priority
+parameter to set the priority of the Arlan 655 Radio Media Access Control
+the values are
+.Cm normal , high
+or
+.Cm highest .
+The higher the priority is set, the more likely this unit will be the first
+to successfully transmit a packet when multiple units are trying
+to transmit at the same time.
+.Pp
+The percentage of units on your network that you set to values other than
+.Cm normal
+should be kept small - 10 percent or less.
+.It Ar iface Cm txretry Ar txretry
+Set transmit retries.
+Default is 0.
+.It Ar iface Cm stat
+Print internal Arlan 655 statistics block.
+.It Ar iface Cm quality
+Display the cached signal level and quality maintained by the
+.Xr arl 4
+driver.
+The driver retains information about quality and level for packets
+received from different hosts.
+Also the driver extracts values from ACK packets.
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent
+arlcontrol arl0 country 11 priority high
+arlcontrol arl0 quality
+arlcontrol arl0 stat
+arlcontrol arl0
+.Ed
+.Pp
+You can configure the Arlan 655 card from
+.Pa /etc/start_if.arl0 .
+For example:
+.Bd -literal -offset indent
+#!/bin/sh
+/usr/sbin/arlcontrol country 9 priority highest
+.Ed
+.Sh SEE ALSO
+.Xr arl 4
+.Rs
+.%T "Arlan 655 ISA Wireless LAN Client Card User Guide"
+.Re
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.3 .
+.Sh BUGS
+Aironet Arlan 640 bridges and Arlan 630 access points do not understand
+country codes other than 9.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.Aq ran@styx.aic.net .
+This manpage was written by
+.An Yuri Kurenkov Aq y.kurenkov@init.ru .
diff --git a/usr.sbin/arlcontrol/arlcontrol.c b/usr.sbin/arlcontrol/arlcontrol.c
new file mode 100644
index 0000000..dd03581
--- /dev/null
+++ b/usr.sbin/arlcontrol/arlcontrol.c
@@ -0,0 +1,431 @@
+/*
+ * $RISS: if_arl/arlconfig/arlconfig.c,v 1.5 2004/03/16 05:00:21 count Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include <dev/arl/if_arlreg.h>
+
+struct freq_list {
+ short fr_no;
+ char* name;
+};
+
+struct freq_list freq_list_1[] = {
+ { 0, "908.50" },
+ { 1, "910.06" },
+ { 2, "915.52" },
+ { 3, "915.00" },
+ { 4, "917.83" },
+ { 5, "919.22" },
+ { 6, "922.26" },
+ { 7, "911.45" },
+ { 8, "915.00" },
+ { 9, "918.55" },
+ { 10,"915.00" },
+ { 11,"915.00" }
+};
+
+struct freq_list freq_list_6[] = {
+ { 0, "920.31" },
+ { 1, "920.33" },
+ { 2, "921.55" },
+ { 3, "922.17" },
+ { 4, "922.79" },
+ { 5, "921.46" },
+ { 6, "921.55" }
+};
+
+struct freq_list freq_list_9[] = {
+ { 0, "Bad" },
+ { 1, "2412" },
+ { 2, "2427" },
+ { 3, "2442" },
+ { 4, "2457" },
+ { 5, "2465" }
+};
+
+
+struct freq_list freq_list_10[] = {
+ { 0, "Bad" },
+ { 1, "2412" },
+ { 2, "2427" },
+ { 3, "2442" },
+ { 4, "2457" },
+ { 5, "2472" }
+};
+
+struct freq_list freq_list_11[] = {
+ { 0, "Bad" },
+ { 1, "2484" }
+};
+
+struct freq_list freq_list_12[] = {
+ { 0, "Bad" },
+ { 1, "2457" },
+ { 2, "2465" },
+ { 3, "2472" },
+};
+
+struct freq_list freq_list_13[] = {
+ { 0, "Bad" },
+ { 1, "2411" },
+ { 2, "2425" },
+ { 3, "2439" }
+};
+
+struct freq_list freq_list_14[] = {
+ { 0, "Bad" },
+ { 1, "2427" },
+ { 2, "2442" },
+ { 3, "2457" }
+};
+
+struct freq_list freq_list_15[] = {
+ { 0, "Bad" },
+ { 1, "2460" }
+};
+
+#define MAXFREQ(a) sizeof(a)/sizeof(struct freq_list)
+
+struct rate_list {
+ short rate_no;
+ char* name;
+};
+
+struct rate_list rate_list_2400[] = {
+ { 0, "Bad" },
+ { 1, "354" },
+ { 2, "512" },
+ { 3, "1000" },
+ { 4, "2000" }
+};
+
+struct radio_type {
+ int id;
+ char* name;
+} radio_type_list [] = {
+ { 0, "No EPROM" },
+ { 1, "092/094" },
+ { 2, "020" },
+ { 3, "092A" },
+ { 4, "020B" },
+ { 5, "095" },
+ { 6, "024" },
+ { 7, "025B" },
+ { 8, "024B" },
+ { 9, "024C" },
+ {10, "025C" },
+ {11, "024-1A" },
+ {12, "025-1A" },
+ {13, "Other" }
+};
+
+static struct ch_list {
+ short chan;
+ char* fr;
+ char* country;
+ struct rate_list* rate;
+ struct freq_list* freq;
+ int max_freq;
+} CHSET[] = {
+ { 0, 0, 0, 0, 0, 0 },
+ { 1, "900 Mhz", "Canada, U.S.A., Mexico", 0, freq_list_1, MAXFREQ(freq_list_1) },
+ { 2, 0, 0, 0, 0, 0 },
+ { 3, 0, 0, 0, 0, 0 },
+ { 4, 0, 0, 0, 0, 0 },
+ { 5, 0, 0, 0, 0, 0 },
+ { 6, "900 Mhz", "Australia", 0, freq_list_6, MAXFREQ(freq_list_6) },
+ { 7, 0, 0, 0, 0, 0 },
+ { 8, 0, 0, 0, 0, 0 },
+ { 9, "2400 Mhz", "North America", rate_list_2400, freq_list_9, MAXFREQ(freq_list_9) },
+ { 10, "2400 Mhz", "E.T.S.I", rate_list_2400, freq_list_10, MAXFREQ(freq_list_10) },
+ { 11, "2400 Mhz", "Japan", rate_list_2400, freq_list_11, MAXFREQ(freq_list_11) },
+ { 12, "2400 Mhz", "France", rate_list_2400, freq_list_12, MAXFREQ(freq_list_12) },
+ { 13, "2400 Mhz", "Australia", rate_list_2400, freq_list_13, MAXFREQ(freq_list_13) },
+ { 14, "2400 Mhz", "Germany", rate_list_2400, freq_list_14, MAXFREQ(freq_list_14) },
+ { 15, "2400 Mhz", "U.K.(MPT1349),Spain", rate_list_2400, freq_list_15, MAXFREQ(freq_list_15) }
+};
+
+char* registrationMode[] = {
+ "NON-TMA",
+ "TMA",
+ "PSP"
+};
+
+char* priorityList[] = {
+ "normal",
+ "high",
+ "highest"
+};
+
+void
+usage()
+{
+ const char *progname = getprogname();
+
+#if 0
+ fprintf(stderr, "\nArlan configuration utility.\n\n");
+#endif
+ fprintf(stderr, "Usage: %s <ifname> [<param> <value> ...]\n", progname);
+ fprintf(stderr, "\t<ifname>\tArlan interface name.\n");
+ fprintf(stderr, "\t<param>\t\tParameter name (see below).\n");
+ fprintf(stderr, "\t<value>\t\tNew value for parameter.\n");
+ fprintf(stderr, "Parameter name:\t\tValue:\n");
+ fprintf(stderr, "\tcountry\t\tset Country (9-15)\n");
+ fprintf(stderr, "\tpriority\tset Priority (normal, high, highest)\n");
+ fprintf(stderr, "\ttxretry\t\tset Arlan Tx retry.\n");
+ fprintf(stderr, "or: %s <ifname> stat\n", progname);
+ fprintf(stderr, "\tprint internal arlan statistics block\n");
+#ifdef ARLCACHE
+ fprintf(stderr,"or: %s <ifname> quality\n", progname);
+ fprintf(stderr,"\tprint receive packet level and quality\n");
+#endif
+ exit(0);
+}
+
+void
+print_al(struct arl_cfg_param *arl_io)
+{
+ printf("Arlan-655(IC2000) type 0x%x v%d.%d, radio module type %s\n",
+ arl_io->hardwareType,
+ arl_io->majorHardwareVersion,
+ arl_io->minorHardwareVersion,
+ (arl_io->radioModule < 13) ?
+ radio_type_list[arl_io->radioModule].name : "Unknown" );
+ printf("\tname %s, sid 0x%06x, mode %s, num tx retry %d\n",
+ arl_io->name,
+ *(int *)arl_io->sid,
+ (arl_io->registrationMode < 3) ?
+ registrationMode[arl_io->registrationMode]:"Unknown",
+ arl_io->txRetry );
+ printf("\tchannel set %d, %s, %s\n",
+ arl_io->channelSet,
+ CHSET[arl_io->channelSet].fr,
+ CHSET[arl_io->channelSet].country);
+ printf("\tfrequency %s Mhz, bitrate %s kb/s, priority %s, receive mode %d\n",
+ (CHSET[arl_io->channelSet].freq &&
+ CHSET[arl_io->channelSet].max_freq > arl_io->channelNumber) ?
+ CHSET[arl_io->channelSet].freq[arl_io->channelNumber].name :
+ "unknown",
+ (CHSET[arl_io->channelSet].rate) ?
+ CHSET[arl_io->channelSet].rate[arl_io->spreadingCode].name :
+ "unknown",
+ arl_io->priority <= 2 ?
+ priorityList[arl_io->priority] : "unknown",
+ arl_io->receiveMode);
+ printf("\tether %s",
+ (char *)ether_ntoa((struct ether_addr *)arl_io->lanCardNodeId));
+ printf(" registered to %s\n",
+ (char *)ether_ntoa((struct ether_addr *)arl_io->specifiedRouter));
+}
+
+void
+print_stb( struct arl_stats stb )
+{
+ printf("Arlan internal statistics block\n\n");
+ printf("%8u\tdatagrams transmitted\n",
+ stb.numDatagramsTransmitted);
+ printf("%8u\tre-transmitted\n",
+ stb.numReTransmissions);
+ printf("%8u\tframes discarded internally in a router\n",
+ stb.numFramesDiscarded);
+ printf("%8u\tdatagrams received\n",
+ stb.numDatagramsReceived);
+ printf("%8u\tduplicate received frame\n",
+ stb.numDuplicateReceivedFrames);
+ printf("%8u\tdatagrams discarded due to unavailable mail box buffer\n",
+ stb.numDatagramsDiscarded);
+ printf("%8d\tmaximum of re-transmissions datagram\n",
+ stb.maxNumReTransmitDatagram);
+ printf("%8d\tmaximum of re-transmissions frame\n",
+ stb.maxNumReTransmitFrames);
+ printf("%8d\tmaximum of consecutive duplicate received frames\n",
+ stb.maxNumConsecutiveDuplicateFrames);
+ printf("%8u\tbytes transmitted\n",
+ stb.numBytesTransmitted);
+ printf("%8u\tbytes received\n",
+ stb.numBytesReceived);
+ printf("%8u\tCRC errors\n",
+ stb.numCRCErrors);
+ printf("%8u\tlength errors\n",
+ stb.numLengthErrors);
+ printf("%8u\tabort errors\n",
+ stb.numAbortErrors);
+ printf("%8u\tTX underuns\n",
+ stb.numTXUnderruns);
+ printf("%8u\tRX overruns\n",
+ stb.numRXOverruns);
+ printf("%8u\tHold Offs (channel tested busy, tx delayed)\n",
+ stb.numHoldOffs);
+ printf("%8u\tframes transmitted\n",
+ stb.numFramesTransmitted);
+ printf("%8u\tframes received\n",
+ stb.numFramesReceived);
+ printf("%8u\treceive frames lost due unavailable buffer\n",
+ stb.numReceiveFramesLost);
+ printf("%8u\tRX buffer overflows \n",
+ stb.numRXBufferOverflows);
+ printf("%8u\tframes discarded due to Address mismatch\n",
+ stb.numFramesDiscardedAddrMismatch);
+ printf("%8u\tframes discarded due to SID mismatch\n",
+ stb.numFramesDiscardedSIDMismatch);
+ printf("%8u\tpolls transmitted\n",
+ stb.numPollsTransmistted);
+ printf("%8u\tpoll acknowledges received\n",
+ stb.numPollAcknowledges);
+ printf("%8u\tstatus vector timeout\n",
+ stb.numStatusVectorTimeouts);
+ printf("%8u\tNACK packets received\n",
+ stb.numNACKReceived);
+}
+
+#ifdef ARLCACHE
+void
+print_qlt(struct arl_sigcache *qlt)
+{
+ int i;
+ u_int8_t zero[6] = {0, 0, 0, 0, 0, 0};
+
+ for (i = 0; i < MAXARLCACHE && bcmp(qlt->macsrc, zero, 6); i++) {
+ printf("[%d]:", i+1);
+ printf(" %02x:%02x:%02x:%02x:%02x:%02x,",
+ qlt->macsrc[0]&0xff,
+ qlt->macsrc[1]&0xff,
+ qlt->macsrc[2]&0xff,
+ qlt->macsrc[3]&0xff,
+ qlt->macsrc[4]&0xff,
+ qlt->macsrc[5]&0xff);
+ printf(" rx lvl/qlty: %d/%d,", qlt->level[ARLCACHE_RX],
+ qlt->quality[ARLCACHE_RX]);
+ printf(" tx lvl/qlty: %d/%d", qlt->level[ARLCACHE_TX],
+ qlt->quality[ARLCACHE_TX]);
+ printf("\n");
+ qlt++;
+ }
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ struct ifreq ifr;
+ struct arl_req arl_io;
+ struct ether_addr *ea;
+ struct arl_stats stb;
+ struct arl_sigcache qlt[MAXARLCACHE];
+ int sd, argind, val = -1;
+ long val2;
+ char *param, *value, *value2;
+
+ if (argc < 2)
+ usage();
+
+ 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;
+ bzero(&arl_io, sizeof(arl_io));
+ ifr.ifr_data = (caddr_t)&arl_io;
+
+ if (argc == 2) {
+ if (ioctl(sd, SIOCGARLALL, (caddr_t)&ifr))
+ err(1,"Get ALL");
+ print_al(&arl_io.cfg);
+ exit(0);
+ }
+
+ if (argc == 3) {
+ if (!strcasecmp(argv[2], "stat")) {
+ strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+ ifr.ifr_data = (caddr_t)&stb;
+ if (ioctl(sd, SIOCGARLSTB, (caddr_t)&ifr))
+ err(1,"Get STB");
+ print_stb(stb);
+ exit(0);
+ }
+#ifdef ARLCACHE
+ if (!strcasecmp( argv[2],"quality")) {
+ printf("\n");
+ strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+ ifr.ifr_data = (caddr_t)qlt;
+ if (ioctl(sd, SIOCGARLQLT, (caddr_t)&ifr))
+ err(1,"Get QLT");
+ print_qlt(qlt);
+ exit(0);
+ }
+#endif
+ }
+
+ arl_io.what_set = 0;
+
+ for (argind = 2; argind < argc; argind += 2) {
+ param = argv[argind];
+ value = argv[argind+1];
+ val = -1;
+
+ if (!strcasecmp(param, "priority")) {
+ if (!strcasecmp(value, "normal"))
+ val = 0;
+ else if (!strcasecmp(value, "high"))
+ val = 1;
+ else if (!strcasecmp(value, "highest"))
+ val = 2;
+ if (val == -1)
+ err( 1, "Bad priority - %s", value);
+ arl_io.cfg.priority = val;
+ arl_io.what_set |= ARLAN_SET_priority;
+ }
+
+ if (!strcasecmp(param, "parent")) {
+ if ((ea = (struct ether_addr*) ether_aton(value)) == NULL)
+ err (1, "Bad parent's MAC - %s", value);
+ for (val = 0; val < 6; val++) {
+ arl_io.cfg.specifiedRouter[val] =
+ (int) ea->octet[val];
+ }
+ arl_io.what_set |= ARLAN_SET_specifiedRouter;
+ }
+
+ if (!strcasecmp(param, "country")) {
+ arl_io.cfg.channelSet = atoi(value);
+ arl_io.what_set |= ARLAN_SET_channelSet;
+ }
+
+ if (!strcasecmp(param, "txretry")) {
+ arl_io.cfg.txRetry = atoi(value);
+ arl_io.what_set |= ARLAN_SET_txRetry;
+ }
+ }
+
+ if (arl_io.what_set) {
+ if (ioctl(sd, SIOCSARLALL, (caddr_t)&ifr))
+ err (1, "Set ALL" );
+ if (ioctl(sd, SIOCGARLALL, (caddr_t)&ifr))
+ err (1, "Get ALL");
+ print_al(&arl_io.cfg);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/arp/Makefile b/usr.sbin/arp/Makefile
new file mode 100644
index 0000000..6ca9dd2
--- /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?= 3
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/arp/arp.4 b/usr.sbin/arp/arp.4
new file mode 100644
index 0000000..4100b3b
--- /dev/null
+++ b/usr.sbin/arp/arp.4
@@ -0,0 +1,176 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)arp4.4 6.5 (Berkeley) 4/18/94
+.\" $FreeBSD$
+.\"
+.Dd June 16, 2004
+.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 for a short period (normally 20 seconds),
+allowing an error to be returned to transmission attempts during this
+interval.
+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).
+An entry for a host which is not responding is a
+.Dq reject
+route (one with the
+.Li RTF_REJECT
+flag set).
+.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.
+It may be enabled by setting the
+.Xr sysctl 8
+MIB variable
+.Va net.link.ether.inet.proxyall
+to 1.
+.Sh DIAGNOSTICS
+.Em "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.
+.Pp
+.Em "arp: ether 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.
+.Pp
+.Em "arp: %d.%d.%d.%d moved from %x:%x:%x:%x:%x:%x to %x:%x:%x:%x:%x:%x" :
+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.
+.Pp
+.Em "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.
+.Pp
+.Em "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.
+.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..7067338
--- /dev/null
+++ b/usr.sbin/arp/arp.8
@@ -0,0 +1,187 @@
+.\" 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.
+.\"
+.\" @(#)arp.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.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
+.Fl a
+.Nm
+.Fl s Ar hostname ether_addr
+.Op Cm temp
+.Op Cm pub Op Cm only
+.Nm
+.Fl S Ar hostname ether_addr
+.Op Cm temp
+.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 to the display operations only.
+.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.
+.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 pub
+.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..0a35859
--- /dev/null
+++ b/usr.sbin/arp/arp.c
@@ -0,0 +1,798 @@
+/*
+ * 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.
+ * 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 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 void 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(u_int32_t ipaddr, struct ether_addr *hwaddr);
+
+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)
+ 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();
+ get(argv[0]);
+ }
+ break;
+ case F_SET:
+ case F_REPLACE:
+ if (argc < 2 || argc > 6)
+ usage();
+ if (func == F_REPLACE)
+ 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, 100, 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) {
+ default:
+ return 0;
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ISO88023:
+ case IFT_ISO88024:
+ case IFT_ISO88025:
+ case IFT_L2VLAN:
+ return 1;
+ }
+}
+
+/*
+ * 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], "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);
+ 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_LLINFO) &&
+ !(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 void
+get(char *host)
+{
+ struct sockaddr_inarp *addr;
+
+ addr = getaddr(host);
+ if (addr == NULL)
+ exit(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");
+ }
+}
+
+/*
+ * 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;
+
+ dst = getaddr(host);
+ if (dst == NULL)
+ return 1;
+ dst->sin_other = do_proxy;
+ for (;;) { /* try twice */
+ rtm = rtmsg(RTM_GET, dst, NULL);
+ 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 &&
+ sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_LLINFO) &&
+ !(rtm->rtm_flags & RTF_GATEWAY) &&
+ valid_type(sdl->sdl_type) )
+ break; /* found it */
+ if (dst->sin_other & SIN_PROXY) {
+ fprintf(stderr, "delete: cannot locate %s\n",host);
+ return (1);
+ }
+ dst->sin_other = SIN_PROXY;
+ }
+ 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, *next;
+ struct rt_msghdr *rtm;
+ struct sockaddr_inarp *sin2;
+ struct sockaddr_dl *sdl;
+ char ifname[IF_NAMESIZE];
+ int found_entry = 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_FLAGS;
+ mib[5] = RTF_LLINFO;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ err(1, "route-sysctl-estimate");
+ if (needed == 0) /* empty table */
+ return 0;
+ if ((buf = malloc(needed)) == NULL)
+ err(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ 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_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_addrs & RTA_NETMASK) {
+ addr = (struct sockaddr_inarp *)
+ (SA_SIZE(sdl) + (char *)sdl);
+ if (addr->sin_addr.s_addr == 0xffffffff)
+ printf(" published");
+ if (addr->sin_len != 8)
+ printf("(weird)");
+ }
+ 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;
+ 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));
+ 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 -a",
+ " arp -s hostname ether_addr [temp] [pub]",
+ " arp -S hostname ether_addr [temp] [pub]",
+ " 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;
+ 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);
+ 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 && rtm->rtm_addrs & (w)) { \
+ bcopy(s, cp, sizeof(*s)); cp += SA_SIZE(s);}
+
+ NEXTADDR(RTA_DST, dst);
+ NEXTADDR(RTA_GATEWAY, sdl);
+ NEXTADDR(RTA_NETMASK, &so_mask);
+
+ 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(u_int32_t ipaddr, struct ether_addr *hwaddr)
+{
+ struct ifreq *ifr, *ifend, *ifp;
+ uint32_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;
+ /* XXX can't we use *ifr instead of ifreq ? */
+ strncpy(ifreq.ifr_name, ifr->ifr_name,
+ sizeof(ifreq.ifr_name));
+ /*
+ * 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..b8e7e4b
--- /dev/null
+++ b/usr.sbin/asf/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= asf
+MAN= asf.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/asf/asf.8 b/usr.sbin/asf/asf.8
new file mode 100644
index 0000000..229d855
--- /dev/null
+++ b/usr.sbin/asf/asf.8
@@ -0,0 +1,112 @@
+.\" 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 June 8, 2003
+.Os
+.Dt ASF 8
+.Sh NAME
+.Nm asf
+.Nd add symbol files
+.Sh SYNOPSIS
+.Nm
+.Op Fl afksx
+.Op Ar modules-path Op Ar outfile
+.Sh DESCRIPTION
+By default,
+.Nm
+reads
+.Xr kldstat 8
+output from standard input and writes to standard output 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
+If
+.Ar modules-path
+is specified,
+.Nm
+uses it for the modules directory instead of the default
+.Pa modules .
+This is useful when building in a non-standard location (i.e., not
+.Pa /usr/src
+and
+.Pa /usr/obj ) .
+.Pp
+If
+.Ar outfile
+is specified,
+.Nm
+writes to it instead of standard output.
+.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, start a
+.Xr kldstat 8
+and read the information from it.
+.It Fl s
+Do not prepend a (guessed) subdirectory of the module path.
+.It Fl x
+Normally
+.Nm
+looks for KLDs with names of the form
+.Ao Ar module Ac Ns Pa .ko.debug .
+The
+.Fl x
+option tells
+.Nm
+to look for KLDs with names of the form
+.Ao Ar module Ac Ns Pa .ko .
+.El
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.2 .
+.Sh AUTHORS
+.An Greg Lehey Aq grog@FreeBSD.org
+.Sh BUGS
+It should be possible to write to an
+.Ar outfile
+without specifying a module path.
+.Sh SEE ALSO
+.Xr gdb 1 ,
+.Xr kldstat 8
diff --git a/usr.sbin/asf/asf.c b/usr.sbin/asf/asf.c
new file mode 100644
index 0000000..704fcac
--- /dev/null
+++ b/usr.sbin/asf/asf.c
@@ -0,0 +1,329 @@
+/*
+ * 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 $ */
+/* $FreeBSD$ */
+
+#define MAXLINE 1024
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <fts.h>
+#include <unistd.h>
+
+#define MAXTOKEN 10
+const char *modules_path; /* path relative to kernel
+ * build directory */
+const char *outfile; /* and where to write the output */
+
+/*
+ * 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).
+ */
+static 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 char *
+findmodule(char *modules_path, const char *module_name)
+{
+ char *const path_argv[2] = { modules_path, NULL };
+ char *module_path = NULL;
+ int module_name_len = strlen(module_name);
+ FTS *fts;
+ FTSENT *ftsent;
+
+ if (modules_path == NULL) {
+ fprintf(stderr,
+ "Can't allocate memory to traverse a path: %s (%d)\n",
+ strerror(errno),
+ errno);
+ exit(1);
+ }
+ fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
+ if (fts == NULL) {
+ fprintf(stderr,
+ "Can't begin traversing path %s: %s (%d)\n",
+ modules_path,
+ strerror(errno),
+ errno);
+ exit(1);
+ }
+ while ((ftsent = fts_read(fts)) != NULL) {
+ if (ftsent->fts_info == FTS_DNR ||
+ ftsent->fts_info == FTS_ERR ||
+ ftsent->fts_info == FTS_NS) {
+ fprintf(stderr,
+ "Error while traversing path %s: %s (%d)\n",
+ modules_path,
+ strerror(errno),
+ errno);
+ exit(1);
+ }
+ if (ftsent->fts_info != FTS_F ||
+ ftsent->fts_namelen != module_name_len ||
+ memcmp(module_name, ftsent->fts_name, module_name_len) != 0)
+ continue;
+ if (asprintf(&module_path,
+ "%.*s",
+ ftsent->fts_pathlen,
+ ftsent->fts_path) == -1) {
+ fprintf(stderr,
+ "Can't allocate memory traversing path %s: %s (%d)\n",
+ modules_path,
+ strerror(errno),
+ errno);
+ exit(1);
+ }
+ break;
+ }
+ if (ftsent == NULL && errno != 0) {
+ fprintf(stderr,
+ "Couldn't complete traversing path %s: %s (%d)\n",
+ modules_path,
+ strerror(errno),
+ errno);
+ exit(1);
+ }
+ fts_close(fts);
+ free(modules_path);
+ return (module_path);
+}
+
+static void
+usage(const char *myname)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "%s [-a] [-f] [-k] [-s] [-x] [modules-path [outfile]]\n\n"
+ "\t-a\tappend to outfile)\n"
+ "\t-f\tfind the module in any subdirectory of module-path\n"
+ "\t-k\ttake input from kldstat(8)\n"
+ "\t-s\tdon't prepend subdir for module path\n"
+ "\t-x\tdon't append \".debug\" to module name\n",
+ myname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buf[MAXLINE];
+ FILE *kldstat;
+ FILE *objcopy;
+ FILE *out; /* output file */
+ char ocbuf[MAXLINE];
+ int tokens; /* number of tokens on line */
+ char basetoken[MAXLINE];
+ int i;
+ const char *filemode = "w"; /* mode for outfile */
+ char cwd[MAXPATHLEN]; /* current directory */
+ const char *debugname = ".debug"; /* some file names end in this */
+ char *token[MAXTOKEN];
+ int nosubdir = 0;
+ int dofind = 0;
+
+ getcwd(cwd, MAXPATHLEN); /* find where we are */
+ kldstat = stdin;
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ if (strcmp(argv[i], "-k") == 0) { /* get input from kldstat(8) */
+ if (!(kldstat = popen("kldstat", "r"))) {
+ perror("Can't start kldstat");
+ return 1;
+ }
+ } else if (strcmp(argv[i], "-a") == 0) /* append to outfile */
+ filemode = "a";
+ else if (strcmp(argv[i], "-x") == 0) /* no .debug extension */
+ debugname = ""; /* nothing */
+ else if (strcmp(argv[i], "-s") == 0) /* no subdir */
+ nosubdir = 1; /* nothing */
+ else if (strcmp(argv[i], "-f") == 0) /* find .ko (recursively) */
+ dofind = 1;
+ else {
+ fprintf(stderr,
+ "Invalid option: %s, aborting\n",
+ argv[i]);
+ usage(argv[0]);
+ return 1;
+ }
+ } else if (modules_path == NULL)
+ modules_path = argv[i];
+ else if (outfile == NULL)
+ outfile = argv[i];
+ else {
+ fprintf(stderr,
+ "Extraneous startup information: \"%s\", aborting\n",
+ argv[i]);
+ usage(argv[0]);
+ return 1;
+ }
+ }
+ if (modules_path == NULL)
+ modules_path = "modules";
+ if (outfile == NULL)
+ outfile = ".asf";
+ if ((out = fopen(outfile, filemode)) == NULL) {
+ fprintf(stderr,
+ "Can't open output file %s: %s (%d)\n",
+ outfile,
+ strerror(errno),
+ errno);
+ return 1;
+ }
+ while (fgets(buf, MAXLINE, kldstat)) {
+ if ((!(strstr(buf, "kernel")))
+ && buf[0] != 'I') {
+ quad_t base;
+ quad_t textaddr;
+ quad_t dataaddr;
+ quad_t bssaddr;
+
+ tokens = tokenize(buf, token, MAXTOKEN);
+ base = strtoll(token[2], NULL, 16);
+ if (!dofind) {
+ strcpy(basetoken, token[4]);
+ basetoken[strlen(basetoken) - 3] = '/';
+ basetoken[strlen(basetoken) - 2] = '\0'; /* cut off the .ko */
+ snprintf(ocbuf,
+ MAXLINE,
+ "/usr/bin/objdump --section-headers %s/%s%s%s",
+ modules_path,
+ nosubdir ? "" : basetoken,
+ token[4],
+ debugname);
+ } else {
+ char *modpath;
+
+ modpath = findmodule(strdup(modules_path), token[4]);
+ if (modpath == NULL)
+ continue;
+ snprintf(ocbuf,
+ MAXLINE,
+ "/usr/bin/objdump --section-headers %s%s",
+ modpath,
+ debugname);
+ free(modpath);
+ }
+ if (!(objcopy = popen(ocbuf, "r"))) {
+ fprintf(stderr,
+ "Can't start %s: %s (%d)\n",
+ ocbuf,
+ strerror(errno),
+ errno);
+ return 1;
+ }
+ while (fgets(ocbuf, MAXLINE, objcopy)) {
+ int octokens;
+ char *octoken[MAXTOKEN];
+
+ octokens = tokenize(ocbuf, octoken, MAXTOKEN);
+ if (octokens > 1) {
+ if (!strcmp(octoken[1], ".text"))
+ textaddr = strtoll(octoken[3], NULL, 16) + base;
+ else if (!strcmp(octoken[1], ".data"))
+ dataaddr = strtoll(octoken[3], NULL, 16) + base;
+ else if (!strcmp(octoken[1], ".bss"))
+ bssaddr = strtoll(octoken[3], NULL, 16) + base;
+ }
+ }
+ if (textaddr) { /* we must have a text address */
+ if (!dofind) {
+ fprintf(out,
+ "add-symbol-file %s/%s/%s%s%s 0x%llx",
+ cwd,
+ modules_path,
+ nosubdir ? "" : basetoken,
+ token[4],
+ debugname,
+ textaddr);
+ } else {
+ char *modpath;
+
+ modpath = findmodule(strdup(modules_path), token[4]);
+ if (modpath == NULL)
+ continue;
+ fprintf(out,
+ "add-symbol-file %s%s 0x%llx",
+ modpath,
+ debugname,
+ textaddr);
+ free(modpath);
+ }
+ if (dataaddr)
+ fprintf(out, " -s .data 0x%llx", dataaddr);
+ if (bssaddr)
+ fprintf(out, " -s .bss 0x%llx", bssaddr);
+ fprintf(out, "\n");
+ }
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/atm/Makefile b/usr.sbin/atm/Makefile
new file mode 100644
index 0000000..2367085
--- /dev/null
+++ b/usr.sbin/atm/Makefile
@@ -0,0 +1,29 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+SUBDIR= atmarpd \
+ scspd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/atm/Makefile.inc b/usr.sbin/atm/Makefile.inc
new file mode 100644
index 0000000..46723c1
--- /dev/null
+++ b/usr.sbin/atm/Makefile.inc
@@ -0,0 +1,26 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/atm/atmarpd/Makefile b/usr.sbin/atm/atmarpd/Makefile
new file mode 100644
index 0000000..1ed613c
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/Makefile
@@ -0,0 +1,36 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+PROG= atmarpd
+MAN= atmarpd.8
+SRCS= atmarpd.c atmarp_config.c atmarp_log.c atmarp_scsp.c \
+ atmarp_subr.c atmarp_timer.c
+
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+LDADD= -latm -lmd
+DPADD= ${LIBATM} ${LIBMD}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/atm/atmarpd/atmarp_config.c b/usr.sbin/atm/atmarpd/atmarp_config.c
new file mode 100644
index 0000000..d6d124e
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_config.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: configuration support
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Configure network interface for ATMARP cache synchronization
+ *
+ * Verify the network interface name and set the appropriate fields
+ * in the ATMARP interface entry.
+ *
+ * Arguments:
+ * netif pointer to network interface name
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_cfg_netif(netif)
+ char *netif;
+{
+ int rc;
+ Atmarp_intf *aip = (Atmarp_intf *)0;
+
+ /*
+ * Get an ATMARP interface block
+ */
+ aip = calloc(1, sizeof(Atmarp_intf));
+ if (aip == NULL)
+ atmarp_mem_err("atmarp_cfg_netif: sizeof(Atmarp_intf)");
+
+ /*
+ * Make sure we're configuring a valid
+ * network interface
+ */
+ rc = verify_nif_name(netif);
+ if (rc == 0) {
+ fprintf(stderr, "%s: \"%s\" is not a valid network interface\n",
+ prog, netif);
+ rc = EINVAL;
+ goto cfg_fail;
+ } else if (rc < 0) {
+ rc = errno;
+ fprintf(stderr, "%s: can't verify network interface \"%s\"\n",
+ prog, netif);
+ goto cfg_fail;
+ }
+
+ /*
+ * Update the interface entry
+ */
+ strcpy(aip->ai_intf, netif);
+ aip->ai_state = AI_STATE_NULL;
+ aip->ai_scsp_sock = -1;
+ LINK2TAIL(aip, Atmarp_intf, atmarp_intf_head, ai_next);
+
+ return(0);
+
+cfg_fail:
+ if (aip)
+ free(aip);
+ return(rc);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_log.c b/usr.sbin/atm/atmarpd/atmarp_log.c
new file mode 100644
index 0000000..4f8d212
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_log.c
@@ -0,0 +1,147 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: logging routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Write a message to atmarpd's log
+ *
+ * Arguments:
+ * level the level (error, info, etc.) of the message
+ * fmt printf-style format string
+ * ... parameters for printf-style use according to fmt
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+#if __STDC__
+atmarp_log(const int level, const char *fmt, ...)
+#else
+atmarp_log(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ /*
+ * In debug mode, just write to stdout
+ */
+ if (atmarp_debug_mode) {
+ vprintf(fmt, ap);
+ printf("\n");
+ return;
+ }
+
+ /*
+ * Check whether we have a log file set up
+ */
+ if (!atmarp_log_file) {
+ /*
+ * Write to syslog
+ */
+ vsyslog(level, fmt, ap);
+ } else {
+ /*
+ * Write to the log file
+ */
+ vfprintf(atmarp_log_file, fmt, ap);
+ fprintf(atmarp_log_file, "\n");
+ }
+
+ va_end(ap);
+}
+
+
+/*
+ * Log a memory error and exit
+ *
+ * Arguments:
+ * cp message to log
+ *
+ * Returns:
+ * exits, does not return
+ *
+ */
+void
+atmarp_mem_err(cp)
+ char *cp;
+{
+ atmarp_log(LOG_CRIT, "out of memory: %s", cp);
+ exit(2);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_scsp.c b/usr.sbin/atm/atmarpd/atmarp_scsp.c
new file mode 100644
index 0000000..6a22217
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_scsp.c
@@ -0,0 +1,778 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: SCSP/ATMARP interface code
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <netatm/uni/uniip_var.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Send the cache for a LIS to SCSP
+ *
+ *
+ * Arguments:
+ * aip pointer to interface block
+ *
+ * Returns:
+ * 0 cache sent to SCSP OK
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_cache(aip, msg)
+ Atmarp_intf *aip;
+ Scsp_if_msg *msg;
+{
+ int i, len, rc = 0;
+ Atmarp *aap;
+ Scsp_if_msg *smp = (Scsp_if_msg *)0;
+ Scsp_atmarp_msg *sap;
+
+ /*
+ * Figure out how big the message needs to be
+ */
+ len = sizeof(Scsp_if_msg_hdr);
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
+ len += sizeof(Scsp_atmarp_msg);
+ }
+ }
+
+ /*
+ * Get memory for the cache message
+ */
+ smp = calloc(1, len);
+ if (smp == NULL)
+ atmarp_mem_err("atmarp_scsp_cache: len");
+ /*
+ * Set header fields in SCSP message
+ */
+ smp->si_type = SCSP_CACHE_RSP;
+ smp->si_proto = SCSP_PROTO_ATMARP;
+ smp->si_len = len;
+ smp->si_tok = msg->si_tok;
+
+ /*
+ * Loop through the cache, adding each entry to the SCSP
+ * Cache Response message
+ */
+ sap = &smp->si_atmarp;
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
+ sap->sa_state = SCSP_ASTATE_NEW;
+ sap->sa_cpa = aap->aa_dstip;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &sap->sa_cha);
+ ATM_ADDR_COPY(&aap->aa_dstatmsub, &sap->sa_csa);
+ sap->sa_key = aap->aa_key;
+ sap->sa_oid = aap->aa_oid;
+ sap->sa_seq = aap->aa_seq;
+ sap++;
+ }
+ }
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aip, (char *)smp, len);
+
+ /*
+ * Free the message
+ */
+ if (smp)
+ free(smp);
+ return(rc);
+}
+
+
+/*
+ * Answer a reqeust for information about a cache entry
+ *
+ * Arguments:
+ * aap pointer to entry
+ * state entry's new state
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_solicit(aip, smp)
+ Atmarp_intf *aip;
+ Scsp_if_msg *smp;
+{
+ int i, rc = 0;
+ Atmarp *aap;
+ Scsp_if_msg *rsp = (Scsp_if_msg *)0;
+
+ /*
+ * Search the interface's ATMARP cache for an entry with
+ * the specified cache key and origin ID
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next) {
+ if (KEY_EQUAL(&aap->aa_key,
+ &smp->si_sum.ss_key) &&
+ OID_EQUAL(&aap->aa_oid,
+ &smp->si_sum.ss_oid))
+ break;
+ }
+ if (aap)
+ break;
+ }
+
+ /*
+ * Get storage for a Solicit Response
+ */
+ rsp = calloc(1, sizeof(Scsp_if_msg));
+ if (rsp == NULL)
+ atmarp_mem_err("atmarp_scsp_solicit: sizeof(Scsp_if_msg)");
+
+ /*
+ * Fill out the Solicit Rsp
+ */
+ rsp->si_type = SCSP_SOLICIT_RSP;
+ rsp->si_proto = smp->si_proto;
+ rsp->si_tok = smp->si_tok;
+
+ if (aap) {
+ /*
+ * Copy fields from the ATMARP entry to the SCSP
+ * Update Request message
+ */
+ rsp->si_rc = SCSP_RSP_OK;
+ rsp->si_len = sizeof(Scsp_if_msg_hdr) +
+ sizeof(Scsp_atmarp_msg);
+ rsp->si_atmarp.sa_state = SCSP_ASTATE_UPD;
+ rsp->si_atmarp.sa_cpa = aap->aa_dstip;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &rsp->si_atmarp.sa_cha);
+ ATM_ADDR_COPY(&aap->aa_dstatmsub, &rsp->si_atmarp.sa_csa);
+ rsp->si_atmarp.sa_key = aap->aa_key;
+ rsp->si_atmarp.sa_oid = aap->aa_oid;
+ rsp->si_atmarp.sa_seq = aap->aa_seq;
+ } else {
+ /*
+ * Entry not found--set return code
+ */
+ rsp->si_rc = SCSP_RSP_NOT_FOUND;
+ rsp->si_len = smp->si_len;
+ rsp->si_sum = smp->si_sum;
+ }
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aip, (char *)rsp, rsp->si_len);
+ free(rsp);
+ return(rc);
+}
+
+
+/*
+ * Send a cache update to SCSP
+ *
+ * Arguments:
+ * aap pointer to entry
+ * state entry's new state
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_update(aap, state)
+ Atmarp *aap;
+ int state;
+{
+ int rc = 0;
+ Atmarp_intf *aip = aap->aa_intf;
+ Scsp_if_msg *smp = (Scsp_if_msg *)0;
+
+ /*
+ * Make sure the connection to SCSP is active
+ */
+ if (aip->ai_state == AI_STATE_NULL) {
+ return(0);
+ }
+
+ /*
+ * Get memory for the cache message
+ */
+ smp = calloc(1, sizeof(Scsp_if_msg));
+ if (smp == NULL)
+ atmarp_mem_err("atmarp_scsp_update: sizeof(Scsp_if_msg)");
+
+ /*
+ * Set header fields in SCSP message
+ */
+ smp->si_type = SCSP_UPDATE_REQ;
+ smp->si_proto = SCSP_PROTO_ATMARP;
+ smp->si_len = sizeof(Scsp_if_msg_hdr) + sizeof(Scsp_atmarp_msg);
+
+ /*
+ * Copy fields from the ATMARP entry to the SCSP
+ * Update Request message
+ */
+ smp->si_atmarp.sa_state = state;
+ smp->si_atmarp.sa_cpa = aap->aa_dstip;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &smp->si_atmarp.sa_cha);
+ ATM_ADDR_COPY(&aap->aa_dstatmsub, &smp->si_atmarp.sa_csa);
+ smp->si_atmarp.sa_key = aap->aa_key;
+ smp->si_atmarp.sa_oid = aap->aa_oid;
+ smp->si_atmarp.sa_seq = aap->aa_seq;
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aap->aa_intf, (char *)smp, smp->si_len);
+
+ free(smp);
+ return(rc);
+}
+
+
+/*
+ * Respond to a Cache Update Indication from SCSP
+ *
+ *
+ * Arguments:
+ * aip pointer to interface control block
+ * smp pointer to message from SCSP
+ *
+ * Returns:
+ * 0 Message processed OK
+ * errno Reason for failure
+ *
+ */
+int
+atmarp_scsp_update_in(aip, smp)
+ Atmarp_intf *aip;
+ Scsp_if_msg *smp;
+{
+ int accept, rc;
+ Atmarp *aap;
+
+ /*
+ * Look up the entry
+ */
+ ATMARP_LOOKUP(aip, smp->si_atmarp.sa_cpa.s_addr, aap);
+
+ /*
+ * Whether we accept the request depends on whether we
+ * already have an entry for it
+ */
+ if (!aap) {
+ /*
+ * We don't have this entry--accept it
+ */
+ accept = 1;
+ } else {
+ /*
+ * We do have an entry for this host--check the
+ * origin ID
+ */
+ if (bcmp(&aip->ai_ip_addr.s_addr,
+ smp->si_atmarp.sa_oid.id,
+ SCSP_ATMARP_ID_LEN) == 0) {
+ /*
+ * The received entry originated with us--
+ * reject it
+ */
+ accept = 0;
+ } else if (bcmp(&aip->ai_ip_addr.s_addr,
+ aap->aa_oid.id,
+ SCSP_ATMARP_ID_LEN) == 0) {
+ /*
+ * We originated the entry we currently have--
+ * only accept the new one if SCSP has higher
+ * priority than the existing entry
+ */
+ accept = aap->aa_origin < UAO_SCSP;
+ } else {
+ /*
+ * Accept the entry if it is more up-to-date
+ * than the existing entry
+ */
+ accept = KEY_EQUAL(&aap->aa_key,
+ &smp->si_atmarp.sa_key) &&
+ OID_EQUAL(&aap->aa_oid,
+ &smp->si_atmarp.sa_oid) &&
+ (aap->aa_seq < smp->si_atmarp.sa_seq);
+ }
+ }
+
+ /*
+ * Add the entry to the cache, if appropriate
+ */
+ if (accept) {
+ if (!aap) {
+ /*
+ * Copy info from SCSP to a new cache entry
+ */
+ aap = calloc(1, sizeof(Atmarp));
+ if (aap == NULL)
+ atmarp_mem_err("atmarp_scsp_update_in: sizeof(Atmarp)");
+
+ aap->aa_dstip = smp->si_atmarp.sa_cpa;
+ aap->aa_dstatm = smp->si_atmarp.sa_cha;
+ aap->aa_dstatmsub = smp->si_atmarp.sa_csa;
+ aap->aa_key = smp->si_atmarp.sa_key;
+ aap->aa_oid = smp->si_atmarp.sa_oid;
+ aap->aa_seq = smp->si_atmarp.sa_seq;
+ aap->aa_intf = aip;
+ aap->aa_origin = UAO_SCSP;
+
+ /*
+ * Add the new entry to our cache
+ */
+ ATMARP_ADD(aip, aap);
+ } else {
+ /*
+ * Update the existing entry
+ */
+ aap->aa_dstip = smp->si_atmarp.sa_cpa;
+ aap->aa_dstatm = smp->si_atmarp.sa_cha;
+ aap->aa_dstatmsub = smp->si_atmarp.sa_csa;
+ aap->aa_key = smp->si_atmarp.sa_key;
+ aap->aa_oid = smp->si_atmarp.sa_oid;
+ aap->aa_seq = smp->si_atmarp.sa_seq;
+ aap->aa_origin = UAO_SCSP;
+ }
+
+ /*
+ * Send the updated entry to the kernel
+ */
+ if (atmarp_update_kernel(aap) == 0)
+ rc = SCSP_RSP_OK;
+ else
+ rc = SCSP_RSP_REJ;
+ } else {
+ rc = SCSP_RSP_REJ;
+ }
+
+ /*
+ * Turn the received message into a response
+ */
+ smp->si_type = SCSP_UPDATE_RSP;
+ smp->si_rc = rc;
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = atmarp_scsp_out(aip, (char *)smp, smp->si_len);
+
+ return(rc);
+}
+
+
+/*
+ * Read and process a message from SCSP
+ *
+ *
+ * Arguments:
+ * aip interface for read
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_read(aip)
+ Atmarp_intf *aip;
+{
+ int len, rc = 0;
+ char *buff = (char *)0;
+ Scsp_if_msg *smp;
+ Scsp_if_msg_hdr msg_hdr;
+
+ /*
+ * Read the header of the message from SCSP
+ */
+ len = read(aip->ai_scsp_sock, (char *)&msg_hdr,
+ sizeof(msg_hdr));
+ if (len == -1) {
+ rc = errno;
+ goto read_fail;
+ } else if (len != sizeof(msg_hdr)) {
+ rc = EMSGSIZE;
+ goto read_fail;
+ }
+
+ /*
+ * Get a buffer that will hold the message
+ */
+ buff = malloc(msg_hdr.sh_len);
+ if (buff == NULL)
+ atmarp_mem_err("atmarp_scsp_read: msg_hdr.sh_len");
+ bcopy(&msg_hdr, buff, sizeof(msg_hdr));
+
+ /*
+ * Read the rest of the message, if there is more than
+ * just a header
+ */
+ len = msg_hdr.sh_len - sizeof(msg_hdr);
+ if (len > 0) {
+ len = read(aip->ai_scsp_sock, buff + sizeof(msg_hdr),
+ len);
+ if (len == -1) {
+ rc = errno;
+ goto read_fail;
+ } else if (len != msg_hdr.sh_len - sizeof(msg_hdr)) {
+ rc = EMSGSIZE;
+ goto read_fail;
+ }
+ }
+
+ /*
+ * Handle the message based on its type
+ */
+ smp = (Scsp_if_msg *)buff;
+ switch(smp->si_type) {
+ case SCSP_CFG_RSP:
+ if (smp->si_rc != SCSP_RSP_OK) {
+ rc = EINVAL;
+ goto read_fail;
+ }
+ break;
+ case SCSP_CACHE_IND:
+ rc = atmarp_scsp_cache(aip, smp);
+ break;
+ case SCSP_SOLICIT_IND:
+ rc = atmarp_scsp_solicit(aip, smp);
+ break;
+ case SCSP_UPDATE_IND:
+ rc = atmarp_scsp_update_in(aip, smp);
+ break;
+ case SCSP_UPDATE_RSP:
+ /*
+ * Ignore Update Responses
+ */
+ break;
+ default:
+ atmarp_log(LOG_ERR, "Unexpected SCSP message received");
+ return(EOPNOTSUPP);
+ }
+ free(buff);
+ return(rc);
+
+read_fail:
+ if (buff)
+ free(buff);
+
+ /*
+ * Error on socket to SCSP--close the socket and set the state
+ * so that we know to retry when the cache timer fires.
+ */
+ atmarp_scsp_close(aip);
+
+ return(rc);
+}
+
+
+/*
+ * Send a message to SCSP
+ *
+ *
+ * Arguments:
+ * aip pointer to ATMARP interface to send message on
+ * buff pointer to message buffer
+ * len length of message
+ *
+ * Returns:
+ * 0 message sent
+ * errno reason for failure
+ *
+ */
+int
+atmarp_scsp_out(aip, buff, len)
+ Atmarp_intf *aip;
+ char *buff;
+ int len;
+{
+ int rc;
+
+ /*
+ * Send the message to SCSP
+ */
+ rc = write(aip->ai_scsp_sock, buff, len);
+ if (rc == len)
+ return(0);
+
+ /*
+ * Error on write--close the socket to SCSP, clean up and
+ * set the state so that we know to retry when the cache
+ * timer fires.
+ */
+ atmarp_scsp_close(aip);
+
+ /*
+ * Set the return code
+ */
+ if (rc < 0) {
+ rc = errno;
+ } else {
+ rc = EFAULT;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Set up a socket and connect to SCSP
+ *
+ * Arguments:
+ * aip pointer to interface block
+ *
+ * Returns:
+ * 0 success, ai_scsp_sock is set
+ * errno reason for failure
+ *
+ *
+ */
+int
+atmarp_scsp_connect(aip)
+ Atmarp_intf *aip;
+{
+ int len, rc, sd;
+ char *sn;
+ Scsp_if_msg cfg_msg;
+
+ static struct sockaddr local_addr = {
+#if (defined(BSD) && (BSD >= 199103))
+ sizeof(struct sockaddr), /* sa_len */
+#endif
+ AF_UNIX, /* sa_family */
+ ATMARP_SOCK_PREFIX /* sa_data */
+ };
+ static struct sockaddr scsp_addr = {
+#if (defined(BSD) && (BSD >= 199103))
+ sizeof(struct sockaddr), /* sa_len */
+#endif
+ AF_UNIX, /* sa_family */
+ SCSPD_SOCK_NAME /* sa_data */
+ };
+
+ /*
+ * Construct a name for the socket
+ */
+ strncpy(local_addr.sa_data, ATMARP_SOCK_PREFIX,
+ sizeof(local_addr.sa_data));
+ (void)strncat(local_addr.sa_data, aip->ai_intf,
+ sizeof(local_addr.sa_data));
+ sn = strdup(local_addr.sa_data);
+ if (!sn)
+ atmarp_mem_err("atmarp_scsp_connect: strdup");
+
+ /*
+ * Clean up any old socket
+ */
+ rc = unlink(sn);
+ if (rc < 0 && errno != ENOENT)
+ return(errno);
+
+ /*
+ * Open a socket to SCSP
+ */
+ sd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sd == -1) {
+ free(sn);
+ return(errno);
+ }
+ if (sd > atmarp_max_socket) {
+ atmarp_max_socket = sd;
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ rc = errno;
+ goto scsp_connect_fail;
+ }
+
+ /*
+ * Bind the local socket address
+ */
+ rc = bind(sd, &local_addr, sizeof(local_addr));
+ if (rc) {
+ rc = errno;
+ goto scsp_connect_fail;
+ }
+
+ /*
+ * Connect to SCSP
+ */
+ rc = connect(sd, &scsp_addr, sizeof(scsp_addr));
+ if (rc) {
+ rc = errno;
+ goto scsp_connect_fail;
+ }
+
+ /*
+ * Save socket information in interface control block
+ */
+ aip->ai_scsp_sock = sd;
+ aip->ai_scsp_sockname = sn;
+ aip->ai_state = AI_STATE_UP;
+
+ /*
+ * Send configuration information to SCSP
+ */
+ bzero(&cfg_msg, sizeof(cfg_msg));
+ cfg_msg.si_type = SCSP_CFG_REQ;
+ cfg_msg.si_proto = SCSP_PROTO_ATMARP;
+ strcpy(cfg_msg.si_cfg.atmarp_netif, aip->ai_intf);
+ len =sizeof(Scsp_if_msg_hdr) + strlen(aip->ai_intf) + 1;
+ cfg_msg.si_len = len;
+ rc = atmarp_scsp_out(aip, (char *)&cfg_msg, len);
+ if (rc) {
+ return(rc);
+ }
+
+ return(0);
+
+scsp_connect_fail:
+ (void)close(sd);
+ aip->ai_scsp_sock = -1;
+ free(sn);
+ aip->ai_scsp_sockname = NULL;
+ aip->ai_state = AI_STATE_NULL;
+ return(rc);
+}
+
+
+/*
+ * Close a socket connection to SCSP
+ *
+ * Arguments:
+ * aip pointer to interface block for connection to be closed
+ *
+ * Returns:
+ * none
+ *
+ *
+ */
+void
+atmarp_scsp_close(aip)
+ Atmarp_intf *aip;
+{
+ /*
+ * Close and unlink the SCSP socket
+ */
+ (void)close(aip->ai_scsp_sock);
+ aip->ai_scsp_sock = -1;
+ (void)unlink(aip->ai_scsp_sockname);
+ free(aip->ai_scsp_sockname);
+ aip->ai_scsp_sockname = NULL;
+
+ aip->ai_state = AI_STATE_NULL;
+
+ return;
+}
+
+
+/*
+ * Disconnect an interface from SCSP
+ *
+ * Arguments:
+ * aip pointer to interface block for connection to be closed
+ *
+ * Returns:
+ * 0 success, ai_scsp_sock is set
+ * errno reason for failure
+ *
+ *
+ */
+int
+atmarp_scsp_disconnect(aip)
+ Atmarp_intf *aip;
+{
+ int i;
+ Atmarp *aap;
+
+ /*
+ * Close and unlink the SCSP socket
+ */
+ atmarp_scsp_close(aip);
+
+ /*
+ * Free the ATMARP cache associated with the interface
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap; aap = aap->aa_next)
+ free(aap);
+ aip->ai_arptbl[i] = (Atmarp *)0;
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_subr.c b/usr.sbin/atm/atmarpd/atmarp_subr.c
new file mode 100644
index 0000000..6987a4b
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_subr.c
@@ -0,0 +1,953 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: misc. subroutines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sigmgr.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <netatm/uni/unisig_var.h>
+#include <netatm/uni/uniip_var.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Find an ATMARP interface, given its socket number
+ *
+ * Arguments:
+ * sd socket descriptor
+ *
+ * Returns:
+ * 0 failure
+ * else pointer to interface associated with socket
+ *
+ */
+Atmarp_intf *
+atmarp_find_intf_sock(sd)
+ int sd;
+{
+ Atmarp_intf *aip;
+
+ /*
+ * Loop through the list of interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (aip->ai_scsp_sock == sd)
+ break;
+ }
+
+ return(aip);
+}
+
+
+/*
+ * Find an ATMARP interface, given its name
+ *
+ * Arguments:
+ * name pointer to network interface name
+ *
+ * Returns:
+ * 0 failure
+ * else pointer to interface associated with name
+ *
+ */
+Atmarp_intf *
+atmarp_find_intf_name(name)
+ char *name;
+{
+ Atmarp_intf *aip;
+
+ /*
+ * Loop through the list of interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (strcmp(name, aip->ai_intf) == 0)
+ break;
+ }
+
+ return(aip);
+}
+
+
+/*
+ * Clear the mark field on all ATMARP cache entries
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+atmarp_clear_marks()
+
+{
+ int i;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * Loop through list of interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ /*
+ * Clear mark on every entry in the interface's cache
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++ ) {
+ for (aap = aip->ai_arptbl[i]; aap;
+ aap = aap->aa_next) {
+ aap->aa_mark = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ * Check whether the host system is an ATMARP server for
+ * the LIS associated with a given interface
+ *
+ * Arguments:
+ * aip pointer to an ATMARP interface control block
+ *
+ * Returns:
+ * 1 host is a server
+ * 0 host is not a server
+ *
+ */
+int
+atmarp_is_server(aip)
+ Atmarp_intf *aip;
+{
+ int rc;
+ size_t buf_len;
+ struct atminfreq air;
+ struct air_asrv_rsp *asrv_info;
+
+ /*
+ * Get interface information from the kernel
+ */
+ strcpy(air.air_int_intf, aip->ai_intf);
+ air.air_opcode = AIOCS_INF_ASV;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_asrv_rsp));
+ if ((ssize_t)buf_len == -1)
+ return(0);
+
+ /*
+ * Check the interface's ATMARP server address
+ */
+ asrv_info = (struct air_asrv_rsp *) air.air_buf_addr;
+ rc = (asrv_info->asp_addr.address_format == T_ATM_ABSENT) &&
+ (asrv_info->asp_subaddr.address_format ==
+ T_ATM_ABSENT);
+ free(asrv_info);
+ return(rc);
+}
+
+
+/*
+ * Check whether an interface is up and ready for service
+ *
+ * Arguments:
+ * aip pointer to network interface block
+ *
+ * Returns:
+ * 0 interface not ready, errno has reason
+ * 1 interface is ready to go (interface block is updated)
+ *
+ */
+int
+atmarp_if_ready(aip)
+ Atmarp_intf *aip;
+{
+ int i, mtu, rc, sel;
+ size_t len;
+ Atmarp *aap = (Atmarp *)0;
+ struct atminfreq air;
+ struct air_netif_rsp *netif_rsp = (struct air_netif_rsp *)0;
+ struct air_int_rsp *intf_rsp = (struct air_int_rsp *)0;
+ struct sockaddr_in *ip_addr;
+ struct sockaddr_in subnet_mask;
+ Atm_addr_nsap *anp;
+
+ /*
+ * Get the IP address and physical interface name
+ * associated with the network interface
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_NIF;
+ strcpy(air.air_netif_intf, aip->ai_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_netif_rsp));
+ if ((ssize_t)len == -1)
+ goto if_ready_fail;
+ netif_rsp = (struct air_netif_rsp *)air.air_buf_addr;
+
+ ip_addr = (struct sockaddr_in *)&netif_rsp->anp_proto_addr;
+ if (ip_addr->sin_family != AF_INET ||
+ ip_addr->sin_addr.s_addr == 0) {
+ errno = EAFNOSUPPORT;
+ goto if_ready_fail;
+ }
+
+ /*
+ * Get the MTU for the network interface
+ */
+ mtu = get_mtu(aip->ai_intf);
+ if (mtu < 0) {
+ goto if_ready_fail;
+ }
+
+
+ /*
+ * Get the subnet mask associated with the
+ * network interface
+ */
+ rc = get_subnet_mask(aip->ai_intf, &subnet_mask);
+ if (rc || subnet_mask.sin_family != AF_INET) {
+ goto if_ready_fail;
+ }
+
+ /*
+ * Get physical interface information
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_INT;
+ strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_int_rsp));
+ if ((ssize_t)len == -1)
+ goto if_ready_fail;
+ intf_rsp = (struct air_int_rsp *)air.air_buf_addr;
+
+ /*
+ * Check the signalling manager
+ */
+ if (intf_rsp->anp_sig_proto != ATM_SIG_UNI30 &&
+ intf_rsp->anp_sig_proto != ATM_SIG_UNI31 &&
+ intf_rsp->anp_sig_proto != ATM_SIG_UNI40) {
+ errno = EINVAL;
+ goto if_ready_fail;
+ }
+
+ /*
+ * Check the interface state
+ */
+ if (intf_rsp->anp_sig_state != UNISIG_ACTIVE) {
+ errno = EINVAL;
+ goto if_ready_fail;
+ }
+
+ /*
+ * Check the address format
+ */
+ if (intf_rsp->anp_addr.address_format != T_ATM_ENDSYS_ADDR &&
+ !(intf_rsp->anp_addr.address_format ==
+ T_ATM_E164_ADDR &&
+ intf_rsp->anp_subaddr.address_format ==
+ T_ATM_ENDSYS_ADDR)) {
+ errno = EINVAL;
+ goto if_ready_fail;
+ }
+
+ /*
+ * Find the selector byte value for the interface
+ */
+ for (i=0; i<strlen(aip->ai_intf); i++) {
+ if (aip->ai_intf[i] >= '0' &&
+ aip->ai_intf[i] <= '9')
+ break;
+ }
+ sel = atoi(&aip->ai_intf[i]);
+
+ /*
+ * Make sure we're the server for this interface's LIS
+ */
+ if (!atmarp_is_server(aip)) {
+ rc = EINVAL;
+ goto if_ready_fail;
+ }
+
+ /*
+ * If we already have the interface active and the address
+ * hasn't changed, return
+ */
+ if (aip->ai_state != AI_STATE_NULL &&
+ bcmp((caddr_t) &((struct sockaddr_in *)
+ &netif_rsp->anp_proto_addr)->sin_addr,
+ (caddr_t)&aip->ai_ip_addr,
+ sizeof(aip->ai_ip_addr)) == 0 &&
+ ATM_ADDR_EQUAL(&intf_rsp->anp_addr,
+ &aip->ai_atm_addr) &&
+ ATM_ADDR_EQUAL(&intf_rsp->anp_subaddr,
+ &aip->ai_atm_subaddr)) {
+ return(1);
+ }
+
+ /*
+ * Delete any existing ATMARP cache entry for this interface
+ */
+ ATMARP_LOOKUP(aip, aip->ai_ip_addr.s_addr, aap);
+ if (aap) {
+ ATMARP_DELETE(aip, aap);
+ free(aap);
+ }
+
+ /*
+ * Update the interface entry
+ */
+ aip->ai_ip_addr = ((struct sockaddr_in *)
+ &netif_rsp->anp_proto_addr)->sin_addr;
+ aip->ai_subnet_mask = subnet_mask.sin_addr;
+ aip->ai_mtu = mtu + 8;
+ ATM_ADDR_COPY(&intf_rsp->anp_addr,
+ &aip->ai_atm_addr);
+ ATM_ADDR_COPY(&intf_rsp->anp_subaddr,
+ &aip->ai_atm_subaddr);
+ anp = (Atm_addr_nsap *)aip->ai_atm_addr.address;
+ if (aip->ai_atm_addr.address_format == T_ATM_ENDSYS_ADDR) {
+ anp->aan_sel = sel;
+ } else if (aip->ai_atm_addr.address_format ==
+ T_ATM_E164_ADDR &&
+ aip->ai_atm_subaddr.address_format ==
+ T_ATM_ENDSYS_ADDR) {
+ anp->aan_sel = sel;
+ }
+
+ /*
+ * Get a new ATMARP cache for the interface
+ */
+ aap = calloc(1, sizeof(Atmarp));
+ if (aap == NULL)
+ atmarp_mem_err("atmarp_if_ready: sizeof(Atmarp)");
+
+ /*
+ * Fill out the entry
+ */
+ aap->aa_dstip = aip->ai_ip_addr;
+ ATM_ADDR_COPY(&intf_rsp->anp_addr, &aap->aa_dstatm);
+ ATM_ADDR_COPY(&intf_rsp->anp_subaddr,
+ &aap->aa_dstatmsub);
+ aap->aa_key.key_len = SCSP_ATMARP_KEY_LEN;
+ scsp_cache_key(&aap->aa_dstatm, &aap->aa_dstip,
+ SCSP_ATMARP_KEY_LEN, aap->aa_key.key);
+ aap->aa_oid.id_len = SCSP_ATMARP_ID_LEN;
+ aap->aa_seq = SCSP_CSA_SEQ_MIN;
+ bcopy(&aap->aa_dstip.s_addr, aap->aa_oid.id, SCSP_ATMARP_ID_LEN);
+ aap->aa_intf = aip;
+ aap->aa_flags = AAF_SERVER;
+ aap->aa_origin = UAO_LOCAL;
+
+ /*
+ * Add the entry to the cache
+ */
+ ATMARP_ADD(aip, aap);
+
+ /*
+ * Free dynamic data
+ */
+ free(netif_rsp);
+ free(intf_rsp);
+ return(1);
+
+if_ready_fail:
+ if (netif_rsp)
+ free(netif_rsp);
+ if (intf_rsp)
+ free(intf_rsp);
+ return(0);
+}
+
+
+/*
+ * Copy an ATMARP cache entry from kernel format into an entry
+ * suitable for our cache
+ *
+ * Arguments:
+ * cp pointer to kernel entry
+ *
+ * Returns:
+ * pointer to a new cache entry
+ * 0 error
+ *
+ */
+Atmarp *
+atmarp_copy_cache_entry(cp)
+ struct air_arp_rsp *cp;
+
+{
+ struct sockaddr_in *ipp;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * Sanity checks
+ */
+ if (!cp)
+ return((Atmarp *)0);
+ aip = atmarp_find_intf_name(cp->aap_intf);
+ if (!aip)
+ return((Atmarp *)0);
+
+ /*
+ * Get a new cache entry
+ */
+ aap = calloc(1, sizeof(Atmarp));
+ if (aap == NULL) {
+ errno = ENOMEM;
+ return(NULL);
+ }
+ aap->aa_intf = aip;
+
+ /*
+ * Copy fields from the kernel entry to the new entry
+ */
+ ipp = (struct sockaddr_in *)&cp->aap_arp_addr;
+ bcopy(&ipp->sin_addr.s_addr, &aap->aa_dstip.s_addr,
+ sizeof(aap->aa_dstip.s_addr));
+ ATM_ADDR_COPY(&cp->aap_addr, &aap->aa_dstatm);
+ ATM_ADDR_COPY(&cp->aap_subaddr, &aap->aa_dstatmsub);
+ if (cp->aap_origin == UAO_PERM)
+ aap->aa_flags |= AAF_PERM;
+ aap->aa_origin = cp->aap_origin;
+
+ /*
+ * Set up fields for SCSP
+ */
+ aap->aa_key.key_len = SCSP_ATMARP_KEY_LEN;
+ scsp_cache_key(&cp->aap_addr, &aap->aa_dstip,
+ SCSP_ATMARP_KEY_LEN, (char *)aap->aa_key.key);
+ aap->aa_oid.id_len = SCSP_ATMARP_ID_LEN;
+ bcopy(&aip->ai_ip_addr.s_addr, aap->aa_oid.id, SCSP_ATMARP_ID_LEN);
+ aap->aa_seq = SCSP_CSA_SEQ_MIN;
+
+ return(aap);
+}
+
+
+/*
+ * Send an updated ATMARP cache entry to the kernel
+ *
+ * Arguments:
+ * aap pointer to updated entry
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+atmarp_update_kernel(aap)
+ Atmarp *aap;
+{
+ int rc = 0, sd;
+ struct atmaddreq aar;
+ struct sockaddr_in *ipp;
+
+ /*
+ * Build ioctl request
+ */
+ bzero(&aar, sizeof(aar));
+ aar.aar_opcode = AIOCS_ADD_ARP;
+ strncpy(aar.aar_arp_intf, aap->aa_intf->ai_intf,
+ sizeof(aar.aar_arp_intf));
+ aar.aar_arp_origin = UAO_SCSP;
+ ATM_ADDR_COPY(&aap->aa_dstatm, &aar.aar_arp_addr);
+ ipp = (struct sockaddr_in *)&aar.aar_arp_dst;
+ ipp->sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ ipp->sin_len = sizeof(struct sockaddr_in);
+#endif
+ ipp->sin_addr = aap->aa_dstip;
+
+ /*
+ * Pass the new mapping to the kernel
+ */
+ sd = socket(AF_ATM, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ return(errno);
+ }
+ if (ioctl(sd, AIOCADD, (caddr_t)&aar) < 0) {
+ rc = errno;
+ }
+
+ (void)close(sd);
+ return(rc);
+}
+
+
+/*
+ * Read the ATMARP cache from the kernel and scan it, processing
+ * all entries
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+atmarp_get_updated_cache()
+{
+ int i, rc;
+ size_t len;
+ struct atminfreq air;
+ struct air_arp_rsp *cp;
+ struct sockaddr_in *ipp;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * Set up the request
+ */
+ air.air_opcode = AIOCS_INF_ARP;
+ air.air_arp_flags = ARP_RESET_REF;
+ ipp = (struct sockaddr_in *)&air.air_arp_addr;
+#if (defined(BSD) && (BSD >= 199103))
+ ipp->sin_len = sizeof(struct sockaddr_in);
+#endif
+ ipp->sin_family = AF_INET;
+ ipp->sin_addr.s_addr = INADDR_ANY;
+
+ /*
+ * Issue an ATMARP information request IOCTL
+ */
+ len = do_info_ioctl(&air, sizeof(struct air_arp_rsp) * 200);
+ if ((ssize_t)len == -1)
+ return;
+
+ /*
+ * Clear marks on all our cache entries
+ */
+ atmarp_clear_marks();
+
+ /*
+ * Loop through the cache, processing each entry
+ */
+ for (cp = (struct air_arp_rsp *) air.air_buf_addr;
+ len > 0;
+ cp++, len -= sizeof(struct air_arp_rsp)) {
+ atmarp_process_cache_entry(cp);
+ }
+
+ /*
+ * Now delete any old entries that aren't in the kernel's
+ * cache any more
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap;
+ aap = aap->aa_next) {
+ /*
+ * Don't delete the entry for the server
+ */
+ if (aap->aa_flags & AAF_SERVER)
+ continue;
+ /*
+ * Delete any entry that isn't marked
+ */
+ if (!aap->aa_mark) {
+ rc = atmarp_scsp_update(aap,
+ SCSP_ASTATE_DEL);
+ if (rc == 0)
+ ATMARP_DELETE(aip, aap);
+ }
+ }
+ }
+ }
+
+ /*
+ * Free the ioctl response
+ */
+ free(air.air_buf_addr);
+}
+
+
+/*
+ * Process an ATMARP cache entry from the kernel. If we already
+ * have the entry in our local cache, update it, otherwise, add
+ * it. In either case, mark our local copy so we know it's still
+ * in the kernel's cache.
+ *
+ * Arguments:
+ * cp pointer to kernel's cache entry
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+atmarp_process_cache_entry(cp)
+ struct air_arp_rsp *cp;
+
+{
+ int rc;
+ struct sockaddr_in *ipp = (struct sockaddr_in *)&cp->aap_arp_addr;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * See whether the entry is for an interface that's
+ * both configured and up
+ */
+ aip = atmarp_find_intf_name(cp->aap_intf);
+ if (!aip || aip->ai_state != AI_STATE_UP)
+ return;
+
+ /*
+ * Make sure the entry is valid
+ */
+ if (!(cp->aap_flags & ARPF_VALID))
+ return;
+
+ /*
+ * See whether we have the entry in our cache already
+ */
+ ATMARP_LOOKUP(aip, ipp->sin_addr.s_addr, aap);
+ if (aap) {
+ /*
+ * We already have this in our cache--update it
+ */
+ aap->aa_mark = 1;
+ if ((cp->aap_flags & ARPF_REFRESH) &&
+ cp->aap_origin != UAO_SCSP) {
+ aap->aa_seq++;
+ rc = atmarp_scsp_update(aap, SCSP_ASTATE_UPD);
+ }
+ } else {
+ /*
+ * This is a new entry--add it to the cache
+ */
+ aap = atmarp_copy_cache_entry(cp);
+ if (!aap)
+ return;
+ ATMARP_ADD(aip, aap);
+ aap->aa_mark = 1;
+ rc = atmarp_scsp_update(aap, SCSP_ASTATE_NEW);
+ }
+
+ return;
+}
+
+
+/*
+ * Print an SCSP ID
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * ip pointer to the SCSP ID to print
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+print_scsp_id(df, ip)
+ FILE *df;
+ Scsp_id *ip;
+{
+ int i;
+
+ fprintf(df, "\t next: %p\n", ip->next);
+ fprintf(df, "\t id_len: %d\n", ip->id_len);
+ fprintf(df, "\t id: 0x");
+ for (i = 0; i < ip->id_len; i++) {
+ fprintf(df, "%0x ", ip->id[i]);
+ }
+ fprintf(df, "\n");
+}
+
+
+/*
+ * Print an SCSP cacke key
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * cp pointer to the cacke key to print
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+print_scsp_cache_key(df, cp)
+ FILE *df;
+ Scsp_ckey *cp;
+{
+ int i;
+
+ fprintf(df, "\t key_len: %d\n", cp->key_len);
+ fprintf(df, "\t key: 0x");
+ for (i = 0; i < cp->key_len; i++) {
+ fprintf(df, "%0x ", cp->key[i]);
+ }
+ fprintf(df, "\n");
+}
+
+
+/*
+ * Print an ATMARP interface entry
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * aip pointer to interface entry
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_atmarp_intf(df, aip)
+ FILE *df;
+ Atmarp_intf *aip;
+{
+ if (!aip) {
+ fprintf(df, "print_atmarp_intf: NULL interface entry address\n");
+ return;
+ }
+
+ fprintf(df, "ATMARP network interface entry at %p\n", aip);
+ fprintf(df, "\tai_next: %p\n", aip->ai_next);
+ fprintf(df, "\tai_intf: %s\n", aip->ai_intf);
+ fprintf(df, "\tai_ip_addr: %s\n",
+ format_ip_addr(&aip->ai_ip_addr));
+ fprintf(df, "\tai_subnet_mask: %s\n",
+ inet_ntoa(aip->ai_subnet_mask));
+ fprintf(df, "\tai_mtu: %d\n", aip->ai_mtu);
+ fprintf(df, "\tai_atm_addr: %s\n",
+ format_atm_addr(&aip->ai_atm_addr));
+ fprintf(df, "\tai_atm_subaddr: %s\n",
+ format_atm_addr(&aip->ai_atm_subaddr));
+ fprintf(df, "\tai_scsp_sock: %d\n", aip->ai_scsp_sock);
+ fprintf(df, "\tai_scsp_sockname: %s\n", aip->ai_scsp_sockname);
+ fprintf(df, "\tai_state: %d\n", aip->ai_state);
+ fprintf(df, "\tai_mark: %d\n", aip->ai_mark);
+}
+
+
+/*
+ * Print an ATMARP cache entry
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * aap pointer to cache entry
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_atmarp_cache(df, aap)
+ FILE *df;
+ Atmarp *aap;
+{
+ if (!aap) {
+ fprintf(df, "print_atmarp_cache: NULL ATMARP entry address\n");
+ return;
+ }
+
+ fprintf(df, "ATMARP entry at %p\n", aap);
+ fprintf(df, "\taa_next: %p\n", aap->aa_next);
+ fprintf(df, "\taa_dstip: %s\n", inet_ntoa(aap->aa_dstip));
+ fprintf(df, "\taa_dstatm: %s\n",
+ format_atm_addr(&aap->aa_dstatm));
+ fprintf(df, "\taa_dstatmsub: %s\n",
+ format_atm_addr(&aap->aa_dstatmsub));
+ fprintf(df, "\taa_key:\n");
+ print_scsp_cache_key(df, &aap->aa_key);
+ fprintf(df, "\taa_oid:\n");
+ print_scsp_id(df, &aap->aa_oid);
+ fprintf(df, "\taa_seq: %ld (0x%lx)\n", aap->aa_seq,
+ aap->aa_seq);
+ fprintf(df, "\taa_intf: %p\n", aap->aa_intf);
+ fprintf(df, "\taa_flags: ");
+ if (aap->aa_flags & AAF_PERM)
+ fprintf(df, "Permanent ");
+ if (aap->aa_flags & AAF_SERVER)
+ fprintf(df, "Server ");
+ fprintf(df, "\n");
+ fprintf(df, "\taa_origin: %d\n", aap->aa_origin);
+ fprintf(df, "\taa_mark: %d\n", aap->aa_mark);
+}
+
+
+/*
+ * Print the entire ATMARP cache
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * aip pointer to interface whose cache is to be printed
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+dump_atmarp_cache(df, aip)
+ FILE *df;
+ Atmarp_intf *aip;
+{
+ int i;
+ Atmarp *aap;
+
+ if (!aip) {
+ fprintf(df, "dump_atmarp_cache: NULL interface address\n");
+ return;
+ }
+
+ fprintf(df, "ATMARP cache for interface %s\n", aip->ai_intf);
+ for (i=0; i<ATMARP_HASHSIZ; i++) {
+ for (aap=aip->ai_arptbl[i]; aap; aap=aap->aa_next) {
+ print_atmarp_cache(df, aap);
+ }
+ }
+}
+
+
+#ifdef NOTDEF
+/*
+ * Print an ATMARP super-LIS entry
+ *
+ * Arguments:
+ * df pointer to a FILE for the dump
+ * asp pointer to super-LIS entry to be printed
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_atmarp_slis(df, asp)
+ FILE *df;
+ Atmarp_slis *asp;
+{
+ Atmarp_intf **aipp;
+
+ if (!asp) {
+ fprintf(df, "print_atmarp_slis: NULL SLIS address\n");
+ return;
+ }
+
+ fprintf(df, "SLIS entry at 0x%0x\n", (u_long)asp);
+ fprintf(df, "\tas_next: 0x%0x\n", (u_long)asp->as_next);
+ fprintf(df, "\tas_name: %s\n", asp->as_name);
+ fprintf(df, "\tas_cnt: %d\n", asp->as_cnt);
+ for (aipp = &asp->as_intfs; *aipp; aipp++) {
+ fprintf(df, "\t%s (%s)\n", (*aipp)->ai_name,
+ (*aipp)->ai_intf);
+ }
+}
+#endif
+
+
+/*
+ * Dump ATMARPD information
+ *
+ * Called as the result of a SIGINT signal.
+ *
+ * Arguments:
+ * sig signal number
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_sigint(sig)
+ int sig;
+{
+ Atmarp_intf *aip;
+ FILE *df;
+ char fname[64];
+ static int dump_no = 0;
+
+ /*
+ * Build a file name
+ */
+ bzero(&fname, sizeof(fname));
+ sprintf(fname, "/tmp/atmarpd.%d.%03d.out", getpid(), dump_no++);
+
+ /*
+ * Open the output file
+ */
+ df = fopen(fname, "w");
+ if (df == (FILE *)0)
+ return;
+
+ /*
+ * Dump the interface control blocks and
+ * associated ATMARP caches
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ print_atmarp_intf(df, aip);
+ fprintf(df, "\n");
+ dump_atmarp_cache(df, aip);
+ fprintf(df, "\n");
+ }
+
+ /*
+ * Close the output file
+ */
+ (void)fclose(df);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_timer.c b/usr.sbin/atm/atmarpd/atmarp_timer.c
new file mode 100644
index 0000000..c1c6cd9
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_timer.c
@@ -0,0 +1,229 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: timer routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Cache update timeout processing
+ *
+ * When the cache update timer fires, we read the cache from the
+ * kernel, update the internal cache, and restart the timer.
+ *
+ * Arguments:
+ * tp pointer to a HARP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_cache_timeout(tp)
+ Harp_timer *tp;
+{
+ Atmarp_intf *aip;
+
+ /*
+ * Verify the status of all configured interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (atmarp_if_ready(aip)) {
+ /*
+ * The interface is up but we don't have
+ * a connection to SCSP--make a connection
+ */
+ if (aip->ai_state == AI_STATE_NULL)
+ (void)atmarp_scsp_connect(aip);
+ } else {
+ /*
+ * The interface is down--disconnect from SCSP
+ */
+ if (aip->ai_state != AI_STATE_NULL)
+ (void)atmarp_scsp_disconnect(aip);
+ }
+ }
+
+ /*
+ * Read the cache from the kernel
+ */
+ atmarp_get_updated_cache();
+
+ /*
+ * Restart the cache update timer
+ */
+ HARP_TIMER(tp, ATMARP_CACHE_INTERVAL, atmarp_cache_timeout);
+}
+
+
+/*
+ * Permanent cache entry timer processing
+ *
+ * Permanent cache entries (entries that are administratively added
+ * and the entry for the server itself) don't ever get refreshed, so
+ * we broadcast updates for them every 10 minutes so they won't get
+ * deleted from the remote servers' caches
+ *
+ * Arguments:
+ * tp pointer to a HARP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_perm_timeout(tp)
+ Harp_timer *tp;
+{
+ int i, rc;
+ Atmarp_intf *aip;
+ Atmarp *aap;
+
+ /*
+ * Loop through all interfaces
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ /*
+ * Loop through the interface's cache
+ */
+ for (i = 0; i < ATMARP_HASHSIZ; i++) {
+ for (aap = aip->ai_arptbl[i]; aap;
+ aap = aap->aa_next) {
+ /*
+ * Find and update permanent entries
+ */
+ if ((aap->aa_flags & (AAF_PERM |
+ AAF_SERVER)) != 0) {
+ aap->aa_seq++;
+ rc = atmarp_scsp_update(aap,
+ SCSP_ASTATE_UPD);
+ }
+ }
+ }
+ }
+
+ /*
+ * Restart the permanent cache entry timer
+ */
+ HARP_TIMER(tp, ATMARP_PERM_INTERVAL, atmarp_perm_timeout);
+}
+
+
+/*
+ * Keepalive timeout processing
+ *
+ * When the keepalive timer fires, we send a NOP to SCSP. This
+ * will help us detect a broken connection.
+ *
+ * Arguments:
+ * tp pointer to a HARP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+atmarp_keepalive_timeout(tp)
+ Harp_timer *tp;
+{
+ Atmarp_intf *aip;
+ Scsp_if_msg *msg;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ aip = (Atmarp_intf *) ((caddr_t)tp -
+ (int)(&((Atmarp_intf *)0)->ai_keepalive_t));
+
+ /*
+ * Get a message buffer
+ *
+ * XXX arr: Previously, the check on the returned value from
+ * the memory allocation routine was checked and _nothing_
+ * resulted from the check (which would cause problems since
+ * the bzero() of NULL is not fun). At the moment, I am having
+ * it soley return -- this should be reviewed again soon.
+ */
+ msg = calloc(1, sizeof(Scsp_if_msg));
+ if (msg == NULL)
+ return;
+
+ /*
+ * Build a NOP message
+ */
+ msg->si_type = SCSP_NOP_REQ;
+ msg->si_proto = SCSP_PROTO_ATMARP;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+
+ /*
+ * Send the message to SCSP
+ */
+ (void)atmarp_scsp_out(aip, (char *)msg, msg->si_len);
+ free(msg);
+
+ /*
+ * Restart the keepalive timer
+ */
+ HARP_TIMER(&aip->ai_keepalive_t, ATMARP_KEEPALIVE_INTERVAL,
+ atmarp_keepalive_timeout);
+}
diff --git a/usr.sbin/atm/atmarpd/atmarp_var.h b/usr.sbin/atm/atmarpd/atmarp_var.h
new file mode 100644
index 0000000..91a235f
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_var.h
@@ -0,0 +1,224 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: control blocks
+ *
+ */
+
+#ifndef _ATMARP_ATMARP_VAR_H
+#define _ATMARP_ATMARP_VAR_H
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+ * Operational constants
+ */
+#define ATMARP_DIR "/tmp"
+#define ATMARP_SOCK_PREFIX "AA_"
+#define ATMARP_CACHE_INTERVAL 50
+#define ATMARP_PERM_INTERVAL 600
+#define ATMARP_KEEPALIVE_INTERVAL 5
+
+
+/*
+ * Macros for manipulating ATMARP tables and entries
+ */
+#define ATMARP_HASHSIZ 19 /* Hash table size */
+
+#define ATMARP_HASH(ip) ((u_long)(ip) % ATMARP_HASHSIZ)
+
+#define ATMARP_ADD(ai, aa) \
+{ \
+ Atmarp **h; \
+ h = &ai->ai_arptbl[ATMARP_HASH((aa)->aa_dstip.s_addr)]; \
+ LINK2TAIL((aa), Atmarp, *h, aa_next); \
+}
+
+#define ATMARP_DELETE(ai, aa) \
+{ \
+ Atmarp **h; \
+ h = &ai->ai_arptbl[ATMARP_HASH((aa)->aa_dstip.s_addr)]; \
+ UNLINK((aa), Atmarp, *h, aa_next); \
+}
+
+#define ATMARP_LOOKUP(ai, ip, aa) \
+{ \
+ for ((aa) = (ai)->ai_arptbl[ATMARP_HASH(ip)]; \
+ (aa); (aa) = (aa)->aa_next) { \
+ if ((aa)->aa_dstip.s_addr == (ip)) \
+ break; \
+ } \
+}
+
+
+/*
+ * Macro to compare originator ID structures
+ */
+#define OID_EQUAL(id1, id2) \
+ (((id1)->id_len == (id2)->id_len) && \
+ (bcmp((caddr_t)(id1)->id, \
+ (caddr_t)(id2)->id, \
+ (id1)->id_len) == 0))
+
+#define KEY_EQUAL(key1, key2) \
+ (((key1)->key_len == (key2)->key_len) && \
+ (bcmp((caddr_t)(key1)->key, \
+ (caddr_t)(key2)->key, \
+ (key1)->key_len) == 0))
+
+
+/*
+ * Interface entry for ATMARP SCSP interface daemon
+ */
+struct atmarp_intf {
+ struct atmarp_intf *ai_next; /* Next chained I/F */
+ char ai_intf[IFNAMSIZ]; /* Network I/F name */
+ struct in_addr ai_ip_addr; /* IP address */
+ struct in_addr ai_subnet_mask; /* Subnet mask */
+ int ai_mtu; /* IP MTU */
+ Atm_addr ai_atm_addr; /* ATM address */
+ Atm_addr ai_atm_subaddr; /* ATM subaddress */
+ int ai_scsp_sock; /* Socket to SCSP */
+ Harp_timer ai_keepalive_t; /* Keepalive timer */
+ char *ai_scsp_sockname; /* Socket name */
+ u_char ai_state; /* Interface state */
+ u_char ai_mark;
+ struct atmarp *ai_arptbl[ATMARP_HASHSIZ]; /* ARP cache */
+};
+typedef struct atmarp_intf Atmarp_intf;
+
+#define AI_STATE_NULL 0
+#define AI_STATE_UP 1
+
+
+/*
+ * Super-LIS control block for ATMARP server daemon
+ */
+struct atmarp_slis {
+ struct atmarp_slis *as_next; /* Next super-LIS */
+ char *as_name; /* Name of super-LIS */
+ int as_cnt; /* LIS count */
+ Atmarp_intf *as_intfs; /* List of intfs */
+};
+typedef struct atmarp_slis Atmarp_slis;
+
+
+/*
+ * ATMARP cache entry format
+ */
+struct atmarp {
+ struct atmarp *aa_next; /* Hash chain link */
+ struct in_addr aa_dstip; /* Destination IP addr */
+ Atm_addr aa_dstatm; /* Destination ATM addr */
+ Atm_addr aa_dstatmsub; /* Destination ATM subaddr */
+ struct scsp_ckey aa_key; /* SCSP cache key */
+ struct scsp_id aa_oid; /* SCSP originator ID */
+ long aa_seq; /* SCSP sequence no. */
+ Atmarp_intf *aa_intf; /* Interface for entry */
+ u_char aa_flags; /* Flags (see below) */
+ u_char aa_origin; /* Entry origin */
+ char aa_mark; /* Mark */
+};
+typedef struct atmarp Atmarp;
+
+/*
+ * ATMARP Entry Flags
+ */
+#define AAF_PERM 0x01 /* Entry is permanent */
+#define AAF_SERVER 0x02 /* Entry is for the server */
+
+
+/*
+ * Global variables
+ */
+extern char *prog;
+extern int atmarp_debug_mode;
+extern int atmarp_max_socket;
+extern Atmarp_intf *atmarp_intf_head;
+extern Atmarp_slis *atmarp_slis_head;
+extern FILE *atmarp_log_file;
+
+
+/*
+ * Function definitions
+ */
+
+/* atmarp_config.c */
+extern int atmarp_cfg_netif(char *);
+
+/* atmarp_log.c */
+#if __STDC__
+extern void atmarp_log(const int, const char *, ...);
+#else
+extern void atmarp_log(int, char *, va_alist);
+#endif
+extern void atmarp_mem_err(char *);
+
+/* atmarp_scsp.c */
+extern int atmarp_scsp_cache(Atmarp_intf *, Scsp_if_msg *);
+extern int atmarp_scsp_update(Atmarp *, int);
+extern int atmarp_scsp_update_in(Atmarp_intf *, Scsp_if_msg *);
+extern int atmarp_scsp_read(Atmarp_intf *);
+extern int atmarp_scsp_out(Atmarp_intf *, char *, int);
+extern int atmarp_scsp_connect(Atmarp_intf *);
+extern void atmarp_scsp_close(Atmarp_intf *);
+extern int atmarp_scsp_disconnect(Atmarp_intf *);
+
+/* atmarp_subr.c */
+extern Atmarp_intf *atmarp_find_intf_sock(int);
+extern Atmarp_intf *atmarp_find_intf_name(char *);
+extern void atmarp_clear_marks();
+extern int atmarp_is_server(Atmarp_intf *);
+extern int atmarp_if_ready(Atmarp_intf *);
+extern Atmarp * atmarp_copy_cache_entry(struct air_arp_rsp *);
+extern int atmarp_update_kernel(Atmarp *);
+extern void atmarp_get_updated_cache();
+extern void atmarp_process_cache_entry(struct air_arp_rsp *);
+extern void print_atmarp_intf(FILE *, Atmarp_intf *);
+extern void print_atmarp_cache(FILE *, Atmarp *);
+extern void dump_atmarp_cache(FILE *, Atmarp_intf *);
+extern void atmarp_sigint(int);
+
+/* atmarp_timer.c */
+extern void atmarp_cache_timeout(Harp_timer *);
+extern void atmarp_perm_timeout(Harp_timer *);
+extern void atmarp_keepalive_timeout(Harp_timer *);
+
+
+#endif /* _ATMARP_ATMARP_VAR_H */
diff --git a/usr.sbin/atm/atmarpd/atmarpd.8 b/usr.sbin/atm/atmarpd/atmarpd.8
new file mode 100644
index 0000000..351b0c1
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarpd.8
@@ -0,0 +1,172 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" @(#) $FreeBSD$
+.\"
+.\"
+.Dd August 4, 1998
+.Dt ATMARPD 8
+.Os
+.Sh NAME
+.Nm atmarpd
+.Nd "ATMARP/SCSP interface daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl l Aq Ar log_file
+.Aq Ar net_intf
+.Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an interface between the ATMARP server in the
+kernel and the SCSP daemon for the Host ATM Research Platform
+(HARP) networking software.
+The
+.Nm
+utility reads the ATMARP cache from the kernel periodically
+and passes any updated entries to
+.Xr scspd 8
+so they will be
+propagated to remote servers.
+It also accepts updated entries that remote servers have sent to
+.Xr scspd 8
+and, if they are
+new or more up to date than current entries, installs them
+in the kernel's ATMARP cache.
+Both
+.Nm
+and
+.Xr scspd 8
+must be running before any ATMARP cache synchronization can take place.
+.Pp
+When
+.Nm
+starts, it parses its command line and puts
+itself into the background.
+.Pp
+The command-line options are:
+.Bl -tag -width "-l <log_file>"
+.It Fl l Aq Ar log_file
+Specify that
+.Nm
+is to write log messages to the
+file named
+.Aq Ar log_file
+rather than to the system log.
+.It Fl d
+Specify that
+.Nm
+is to be run in debug mode.
+In debug mode,
+.Nm
+is not put into the background.
+Log messages are written to standard output instead of to
+the log file.
+.It Aq Ar net_intf
+Specify the network interface(s) for which the host is providing
+ATMARP service and whose caches are to be synchronized using SCSP.
+If multiple network interface names are specified,
+.Nm
+will provide an interface to
+.Xr scspd 8
+for the servers on all the
+specified interfaces.
+.El
+.Sh SIGNAL PROCESSING
+The following signals can be used to control
+.Nm :
+.Bl -tag -width indent
+.It Dv SIGINT
+Dump debugging information to a file.
+When it receives a
+.Dv SIGINT
+signal,
+.Nm
+dumps a summary of
+its control blocks to a text file (see
+.Sx FILES ) .
+.El
+.Sh FILES
+.Bl -tag -width indent
+.It Xo
+.Sm off
+.Pa /tmp/atmarpd.
+.Aq Ar pid
+.Pa \&.
+.Aq Ar seq
+.Pa .out
+.Sm on
+.Xc
+Debugging information dump file name.
+The
+.Nm
+utility writes a summary of its control blocks to this file
+when it receives a
+.Dv SIGINT
+signal.
+.Aq Ar pid
+is the process ID of the daemon and
+.Aq Ar seq
+is a sequence
+number which is incremented every time a dump is taken.
+.El
+.Sh SEE ALSO
+.Xr atm 8 ,
+.Xr scspd 8
+.Rs
+.%T "Classical IP and ARP over ATM"
+.%O "RFC 1577"
+.Re
+.Rs
+.%T "Classical IP and ARP over ATM"
+.%O "RFC 2225"
+.Re
+.Rs
+.%T "Server Cache Synchronization Protocol (SCSP)"
+.%O "RFC 2334"
+.Re
+.Rs
+.%T "A Distributed ATMARP Service Using SCSP"
+.%O "draft\-ietf\-ion\-scsp\-atmarpd\-00.txt"
+.Re
+.Sh BUGS
+Results are unpredictable if multiple instantiations of
+.Nm
+are run simultaneously for a given network interface.
+.Pp
+Please report any bugs to
+.Aq harp\-bugs@magic.net .
+.Sh COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+.Sh AUTHORS
+.An John Cavanaugh ,
+Network Computing Services, Inc.
+.An Mike Spengler ,
+Network Computing Services, Inc.
+.An Joe Thomas ,
+Network Computing Services, Inc.
+.Sh ACKNOWLEDGMENTS
+This software was developed with the support of the Defense
+Advanced Research Projects Agency (DARPA).
diff --git a/usr.sbin/atm/atmarpd/atmarpd.c b/usr.sbin/atm/atmarpd/atmarpd.c
new file mode 100644
index 0000000..2cb5f70
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarpd.c
@@ -0,0 +1,412 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: main line code
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ttycom.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+char *prog;
+int atmarp_debug_mode = 0;
+int atmarp_max_socket = 0;
+Atmarp_intf *atmarp_intf_head = (Atmarp_intf *)0;
+Atmarp_slis *atmarp_slis_head = (Atmarp_slis *)0;
+FILE *atmarp_log_file = (FILE *)0;
+char *atmarp_log_file_name = (char *)0;
+Harp_timer cache_timer, perm_timer;
+
+
+/*
+ * Print a usage message
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * exits, does not return
+ *
+ */
+void
+usage()
+{
+ fprintf(stderr, "usage: %s [-d] [-l <log_file>] <net_intf> ...\n", prog);
+ exit(1);
+}
+
+
+/*
+ * Process command line parameters
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+initialize(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int i, rc;
+
+ /*
+ * Save program name, ignoring any path components
+ */
+ if ((prog = (char *)strrchr(argv[0], '/')) != NULL)
+ prog++;
+ else
+ prog = argv[0];
+
+ /*
+ * Make sure we're being invoked by the super user
+ */
+ i = getuid();
+ if (i != 0) {
+ fprintf(stderr, "%s: You must be root to run this program\n",
+ prog);
+ exit(1);
+ }
+
+ /*
+ * Scan arguments, checking for options
+ */
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ if (strcmp(argv[i], "-d") == 0) {
+ atmarp_debug_mode = TRUE;
+ } else if (strcmp(argv[i], "-l") == 0) {
+ i++;
+ if (i >= argc) {
+ fprintf(stderr, "%s: Log file name missing\n",
+ prog);
+ exit(1);
+ }
+ atmarp_log_file_name = argv[i];
+ } else {
+ fprintf(stderr, "%s: Unrecognized option \"%s\"\n",
+ prog, argv[i]);
+ exit(1);
+ }
+ } else {
+ /*
+ * Parameter is a network interface name
+ */
+ rc = atmarp_cfg_netif(argv[i]);
+ if (rc) {
+ fprintf(stderr, "%s: Error configuring network interface %s\n",
+ prog, argv[i]);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Make sure we had at least one interface configured
+ */
+ if (!atmarp_intf_head) {
+ usage();
+ }
+}
+
+
+/*
+ * Daemon housekeeping
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+start_daemon()
+
+{
+ int dpid, fd, file_count, rc;
+
+ /*
+ * Ignore selected signals
+ */
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /*
+ * Skip putting things into the background if we're
+ * in debugging mode
+ */
+ if (atmarp_debug_mode)
+ goto daemon_bypass;
+
+ /*
+ * Set up syslog for error logging
+ */
+ if (!atmarp_log_file) {
+ openlog(prog, LOG_PID | LOG_CONS, LOG_DAEMON);
+ }
+
+ /*
+ * Put the daemon into the background
+ */
+ dpid = fork();
+ if (dpid < 0) {
+ atmarp_log(LOG_ERR, "fork failed");
+ exit(1);
+ }
+ if (dpid > 0) {
+ /*
+ * This is the parent process--just exit and let
+ * the daughter do all the work
+ */
+ exit(0);
+ }
+
+ /*
+ * Disassociate from any controlling terminal
+ */
+ rc = setpgrp(0, getpid());
+ if (rc < 0) {
+ atmarp_log(LOG_ERR, "can't change process group");
+ exit(1);
+ }
+ fd = open(_PATH_TTY, O_RDWR);
+ if (fd >= 0) {
+ ioctl(fd, TIOCNOTTY, (char *)0);
+ close(fd);
+ }
+
+ /*
+ * Close all open file descriptors
+ */
+ file_count = getdtablesize();
+ for (fd=0; fd<file_count; fd++) {
+ close(fd);
+ }
+
+ /*
+ * Open log file, if specified
+ */
+ if (atmarp_log_file_name) {
+ atmarp_log_file = fopen(atmarp_log_file_name, "a");
+ if (!atmarp_log_file) {
+ atmarp_log(LOG_ERR, "%s: Can't open log file \'%s\'\n",
+ prog, atmarp_log_file_name);
+ exit(1);
+ }
+ }
+
+ /*
+ * Set up and start interval timer
+ */
+daemon_bypass:
+ init_timer();
+
+ /*
+ * Move to a safe directory
+ */
+ chdir(ATMARP_DIR);
+
+ /*
+ * Clear the file mode creation mask
+ */
+ umask(0);
+
+
+ /*
+ * Set up signal handlers
+ */
+ if (signal(SIGINT, atmarp_sigint) == SIG_ERR) {
+ atmarp_log(LOG_ERR, "SIGINT signal setup failed");
+ exit(1);
+ }
+}
+
+
+/*
+ * Main line code
+ *
+ * The ATMARP server resides in the kernel, while SCSP runs as a daemon
+ * in user space. This program exists to provide an interface between
+ * the two. It periodically polls the kernel to get the ATMARP cache
+ * and passes information about new entries to SCSP. It also accepts
+ * new information from SCSP and passes it to the kernel.
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int i, rc;
+ fd_set read_set, write_set, except_set;
+ Atmarp_intf *aip;
+
+ /*
+ * Process command line arguments
+ */
+ initialize(argc, argv);
+
+ /*
+ * Put the daemon into the background
+ */
+ start_daemon();
+
+ /*
+ * Start the cache update timer
+ */
+ HARP_TIMER(&cache_timer, ATMARP_CACHE_INTERVAL,
+ atmarp_cache_timeout);
+
+ /*
+ * Start the permanent cache entry timer
+ */
+ HARP_TIMER(&perm_timer, ATMARP_PERM_INTERVAL,
+ atmarp_perm_timeout);
+
+ /*
+ * Establish a connection to SCSP for each interface. If a
+ * connect fails, it will be retried when the cache update
+ * timer fires.
+ */
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (atmarp_if_ready(aip)) {
+ (void)atmarp_scsp_connect(aip);
+ }
+ }
+
+ /*
+ * Read the cache from the kernel
+ */
+ atmarp_get_updated_cache();
+
+ /*
+ * Main program loop -- wait for data to come in from SCSP.
+ * When the timer fires, it will be handled elsewhere.
+ */
+ while (1) {
+ /*
+ * Wait for input from SCSP
+ */
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_ZERO(&except_set);
+ for (aip = atmarp_intf_head; aip; aip = aip->ai_next) {
+ if (aip->ai_scsp_sock != -1) {
+ FD_SET(aip->ai_scsp_sock, &read_set);
+ }
+ }
+ rc = select(atmarp_max_socket + 1,
+ &read_set, &write_set,
+ &except_set, (struct timeval *)0);
+ if (rc < 0) {
+ if (harp_timer_exec) {
+ timer_proc();
+ continue;
+ } else if (errno == EINTR) {
+ continue;
+ } else {
+ atmarp_log(LOG_ERR, "Select failed");
+ abort();
+ }
+ }
+
+ /*
+ * Read and process the input from SCSP
+ */
+ for (i = 0; i <= atmarp_max_socket; i++) {
+ if (FD_ISSET(i, &read_set)) {
+ aip = atmarp_find_intf_sock(i);
+ if (aip)
+ rc = atmarp_scsp_read(aip);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/atm/scspd/Makefile b/usr.sbin/atm/scspd/Makefile
new file mode 100644
index 0000000..7130c2b
--- /dev/null
+++ b/usr.sbin/atm/scspd/Makefile
@@ -0,0 +1,41 @@
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $FreeBSD$
+
+PROG= scspd
+MAN= scspd.8
+SRCS= scspd.c scsp_cafsm.c scsp_config.c scsp_config_lex.c \
+ scsp_config_parse.y \
+ scsp_hfsm.c scsp_if.c scsp_input.c scsp_log.c scsp_msg.c \
+ scsp_output.c scsp_print.c scsp_socket.c scsp_subr.c \
+ scsp_timer.c
+
+CFLAGS+= -I. -I${.CURDIR}/../../../sys -I${.CURDIR}
+
+LDADD= -latm -lmd
+DPADD= ${LIBATM} ${LIBMD}
+
+YFLAGS= -d
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/atm/scspd/scsp_cafsm.c b/usr.sbin/atm/scspd/scsp_cafsm.c
new file mode 100644
index 0000000..05a1e24
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_cafsm.c
@@ -0,0 +1,1439 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Cache Alignment finite state machine
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * CA FSM actions
+ */
+#define CA_ACTION_CNT 20
+int scsp_ca_act_00(Scsp_dcs *, void *);
+int scsp_ca_act_01(Scsp_dcs *, void *);
+int scsp_ca_act_02(Scsp_dcs *, void *);
+int scsp_ca_act_03(Scsp_dcs *, void *);
+int scsp_ca_act_04(Scsp_dcs *, void *);
+int scsp_ca_act_05(Scsp_dcs *, void *);
+int scsp_ca_act_06(Scsp_dcs *, void *);
+int scsp_ca_act_07(Scsp_dcs *, void *);
+int scsp_ca_act_08(Scsp_dcs *, void *);
+int scsp_ca_act_09(Scsp_dcs *, void *);
+int scsp_ca_act_10(Scsp_dcs *, void *);
+int scsp_ca_act_11(Scsp_dcs *, void *);
+int scsp_ca_act_12(Scsp_dcs *, void *);
+int scsp_ca_act_13(Scsp_dcs *, void *);
+int scsp_ca_act_14(Scsp_dcs *, void *);
+int scsp_ca_act_15(Scsp_dcs *, void *);
+int scsp_ca_act_16(Scsp_dcs *, void *);
+int scsp_ca_act_17(Scsp_dcs *, void *);
+int scsp_ca_act_18(Scsp_dcs *, void *);
+int scsp_ca_act_19(Scsp_dcs *, void *);
+
+static int (*scsp_ca_act_vec[CA_ACTION_CNT])() = {
+ scsp_ca_act_00,
+ scsp_ca_act_01,
+ scsp_ca_act_02,
+ scsp_ca_act_03,
+ scsp_ca_act_04,
+ scsp_ca_act_05,
+ scsp_ca_act_06,
+ scsp_ca_act_07,
+ scsp_ca_act_08,
+ scsp_ca_act_09,
+ scsp_ca_act_10,
+ scsp_ca_act_11,
+ scsp_ca_act_12,
+ scsp_ca_act_13,
+ scsp_ca_act_14,
+ scsp_ca_act_15,
+ scsp_ca_act_16,
+ scsp_ca_act_17,
+ scsp_ca_act_18,
+ scsp_ca_act_19
+};
+
+/*
+ * CA FSM state table
+ */
+static int ca_state_table[SCSP_CAFSM_EVENT_CNT][SCSP_CAFSM_STATE_CNT] = {
+ /* 0 1 2 3 4 5 */
+ { 1, 1, 1, 1, 1, 1 }, /* 0 */
+ { 2, 2, 2, 2, 2, 2 }, /* 1 */
+ { 0, 3, 4, 5, 15, 15 }, /* 2 */
+ { 0, 17, 17, 17, 7, 7 }, /* 3 */
+ { 0, 17, 17, 17, 8, 8 }, /* 4 */
+ { 0, 17, 17, 17, 10, 10 }, /* 5 */
+ { 0, 6, 6, 0, 9, 9 }, /* 6 */
+ { 0, 0, 0, 0, 12, 12 }, /* 7 */
+ { 0, 0, 0, 0, 13, 13 }, /* 8 */
+ { 18, 14, 14, 14, 11, 11 }, /* 9 */
+ { 0, 19, 0, 0, 16, 16 }, /* 10 */
+};
+
+
+/*
+ * Cache Alignment finite state machine
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block for the neighbor
+ * event the event which has occurred
+ * p pointer to further parameter, if there is one
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_cafsm(dcsp, event, p)
+ Scsp_dcs *dcsp;
+ int event;
+ void *p;
+{
+ int action, rc, state;
+
+ /*
+ * Select an action from the state table
+ */
+ state = dcsp->sd_ca_state;
+ action = ca_state_table[event][state];
+ if (scsp_trace_mode & SCSP_TRACE_CAFSM) {
+ scsp_trace("CAFSM: state=%d, event=%d, action=%d\n",
+ state, event, action);
+ }
+ if (action >= CA_ACTION_CNT || action < 0) {
+ scsp_log(LOG_ERR, "CA FSM--invalid action state=%d, event=%d, action=%d",
+ state, event, action);
+ abort();
+ }
+
+ /*
+ * Perform the selected action
+ */
+ rc = scsp_ca_act_vec[action](dcsp, p);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 0
+ * Unexpected action -- log an error message and go to Master/Slave
+ * Negotiation. The unexpected action is probably from a protocol
+ * error.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * EOPNOTSUPP always returns EOPNOTSUPP
+ *
+ */
+int
+scsp_ca_act_00(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+
+ /*
+ * Log an error message
+ */
+ scsp_log(LOG_ERR, "CA FSM error--unexpected action, state=%d",
+ dcsp->sd_ca_state);
+
+ /*
+ * Set the new state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_NEG;
+
+ /*
+ * Clear out the DCS block
+ */
+ scsp_dcs_cleanup(dcsp);
+
+ /*
+ * Notify the client I/F FSM
+ */
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_DOWN, (Scsp_msg *)0,
+ (Scsp_if_msg *)0);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 1
+ * Hello FSM has reached Bidirectional state -- go to Master/Slave
+ * Negotiation state, make a copy of the client's cache, send first CA
+ * message.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_01(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int i, rc;
+ Scsp_cse *csep, *dupp;
+
+ /*
+ * Set the new state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_NEG;
+
+ /*
+ * Make a copy of client's cache entries for cache alignment
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (csep = dcsp->sd_server->ss_cache[i];
+ csep; csep = csep->sc_next) {
+ dupp = scsp_dup_cse(csep);
+ LINK2TAIL(dupp, Scsp_cse, dcsp->sd_ca_csas,
+ sc_next);
+ }
+ }
+
+ /*
+ * Select an initial sequence number
+ */
+ dcsp->sd_ca_seq = (int)time((time_t *)0);
+
+ /*
+ * Send a CA message
+ */
+ rc = scsp_send_ca(dcsp);
+ if (rc == 0) {
+ HARP_TIMER(&dcsp->sd_ca_rexmt_t, dcsp->sd_ca_rexmt_int,
+ scsp_ca_retran_timeout);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 2
+ * Hello FSM has gone down -- go to Down state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_02(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+
+ /*
+ * Set the new state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_DOWN;
+
+ /*
+ * Clear out the DCS block
+ */
+ scsp_dcs_cleanup(dcsp);
+
+ /*
+ * Notify the client I/F FSM
+ */
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_DOWN, (Scsp_msg *)0,
+ (Scsp_if_msg *)0);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 3
+ * CA message received -- select Cache Summarize Master or Slave state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_03(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc = 0;
+ Scsp_msg *msg = (Scsp_msg *)p;
+
+ /*
+ * Check for slave role for LS
+ */
+ if (msg->sc_ca->ca_m &&
+ msg->sc_ca->ca_i &&
+ msg->sc_ca->ca_o &&
+ msg->sc_ca->ca_mcp.rec_cnt == 0 &&
+ scsp_cmp_id(&msg->sc_ca->ca_mcp.sid,
+ &msg->sc_ca->ca_mcp.rid) > 0) {
+
+ /*
+ * Stop the retransmit timer
+ */
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+
+ /*
+ * Set the new state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_SLAVE;
+ (void)scsp_cfsm(dcsp, SCSP_CIFSM_CA_SUMM,
+ (Scsp_msg *)0, (Scsp_if_msg *)0);
+
+ /*
+ * Save the master's sequence number
+ */
+ dcsp->sd_ca_seq = msg->sc_ca->ca_seq;
+
+ /*
+ * Send a CA message
+ */
+ rc = scsp_send_ca(dcsp);
+ } else
+ /*
+ * Check for master role for LS
+ */
+ if (!msg->sc_ca->ca_m &&
+ !msg->sc_ca->ca_i &&
+ scsp_cmp_id(&msg->sc_ca->ca_mcp.sid,
+ &msg->sc_ca->ca_mcp.rid) < 0) {
+ /*
+ * Stop the retransmit timer
+ */
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+
+ /*
+ * Set the new state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_MASTER;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_SUMM,
+ (Scsp_msg *)0, (Scsp_if_msg *)0);
+
+ /*
+ * Process the CA message
+ */
+ scsp_process_ca(dcsp, msg->sc_ca);
+
+ /*
+ * Increment the sequence number
+ */
+ dcsp->sd_ca_seq++;
+
+ /*
+ * Send a CA in reply
+ */
+ rc = scsp_send_ca(dcsp);
+ if (rc == 0) {
+ HARP_TIMER(&dcsp->sd_ca_rexmt_t,
+ dcsp->sd_ca_rexmt_int,
+ scsp_ca_retran_timeout);
+ }
+ } else {
+ /*
+ * Ignore the message, go to Master/Slave Negotiation
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_NEG;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 4
+ * CA message received while in Cache Summarize Master state -- process
+ * CA message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_04(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc = 0;
+ Scsp_msg *msg = (Scsp_msg *)p;
+
+ /*
+ * If the other side thinks he's the master, or if the
+ * initialization bit is set, or if the message is out
+ * of sequence, go back to Master/Slave Negotiation state
+ */
+ if (msg->sc_ca->ca_m || msg->sc_ca->ca_i ||
+ msg->sc_ca->ca_seq < dcsp->sd_ca_seq - 1 ||
+ msg->sc_ca->ca_seq > dcsp->sd_ca_seq) {
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ dcsp->sd_ca_state = SCSP_CAFSM_NEG;
+ scsp_dcs_cleanup(dcsp);
+ return(scsp_ca_act_01(dcsp, (Scsp_msg *)0));
+ }
+
+ /*
+ * Ignore any duplicate messages
+ */
+ if (msg->sc_ca->ca_seq == dcsp->sd_ca_seq - 1) {
+ return(0);
+ }
+
+ /*
+ * Stop the retransmission timer
+ */
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+
+ /*
+ * Process the CA message
+ */
+ scsp_process_ca(dcsp, msg->sc_ca);
+
+ /*
+ * Increment the CA sequence number
+ */
+ dcsp->sd_ca_seq++;
+
+ /*
+ * If we have no more CSAS records to send and the slave sent
+ * a message with the 'O' bit off, we're done with Summarize
+ * state
+ */
+ if (!dcsp->sd_ca_csas && !msg->sc_ca->ca_o) {
+ /*
+ * Free any CA message saved for retransmission
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
+ }
+
+ /*
+ * If the CRL is empty, we go directly to Aligned state;
+ * otherwise, we go to Update Cache and send a CSUS
+ */
+ if (!dcsp->sd_crl) {
+ /*
+ * Go to Aligned state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_ALIGNED;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_ALIGN,
+ (Scsp_msg *)0,
+ (Scsp_if_msg *)0);
+ } else {
+ /*
+ * Go to Cache Update state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_UPDATE;
+ (void)scsp_cfsm(dcsp, SCSP_CIFSM_CA_UPD,
+ (Scsp_msg *)0,
+ (Scsp_if_msg *)0);
+ rc = scsp_send_csus(dcsp);
+ }
+ } else {
+ /*
+ * There are more CSAS records to be exchanged--
+ * continue the cache exchange
+ */
+ rc = scsp_send_ca(dcsp);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 5
+ * CA message received while in Cache Summarize Slave state -- process
+ * CA message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_05(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc = 0;
+ Scsp_msg *msg = (Scsp_msg *)p;
+
+ /*
+ * If the other side thinks we're the master, or if the
+ * initialization bit is set, or if the message is out
+ * of sequence, go back to Master/Slave Negotiation state
+ */
+ if (!msg->sc_ca->ca_m || msg->sc_ca->ca_i ||
+ msg->sc_ca->ca_seq < dcsp->sd_ca_seq ||
+ msg->sc_ca->ca_seq > dcsp->sd_ca_seq + 1) {
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ dcsp->sd_ca_state = SCSP_CAFSM_NEG;
+ scsp_dcs_cleanup(dcsp);
+ return(scsp_ca_act_01(dcsp, (Scsp_msg *)0));
+ }
+
+ /*
+ * If this is a duplicate, retransmit the last message
+ */
+ if (msg->sc_ca->ca_seq == dcsp->sd_ca_seq) {
+ if (dcsp->sd_ca_rexmt_msg) {
+ rc = scsp_send_msg(dcsp, dcsp->sd_ca_rexmt_msg);
+ if (rc == 0) {
+ HARP_TIMER(&dcsp->sd_ca_rexmt_t,
+ dcsp->sd_ca_rexmt_int,
+ scsp_ca_retran_timeout);
+ }
+ }
+ return(rc);
+ }
+
+ /*
+ * Free the last CA message
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
+ }
+
+ /*
+ * Process the CA message
+ */
+ scsp_process_ca(dcsp, msg->sc_ca);
+
+ /*
+ * Increment the CA sequence number
+ */
+ dcsp->sd_ca_seq++;
+
+ /*
+ * Answer the CA message
+ */
+ rc = scsp_send_ca(dcsp);
+ if (rc)
+ return(rc);
+
+ /*
+ * If we're done sending CSAS records and the other side is,
+ * too, we're done with Summarize state
+ */
+ if (!dcsp->sd_ca_csas && !msg->sc_ca->ca_o) {
+ /*
+ * If the CRL is empty, we go directly to Aligned state;
+ * otherwise, we go to Update Cache and send a CSUS
+ */
+ if (!dcsp->sd_crl) {
+ /*
+ * Go to Aligned state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_ALIGNED;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_ALIGN,
+ (Scsp_msg *)0,
+ (Scsp_if_msg *)0);
+ } else {
+ /*
+ * Go to Cache Update state
+ */
+ dcsp->sd_ca_state = SCSP_CAFSM_UPDATE;
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ HARP_TIMER(&dcsp->sd_ca_rexmt_t,
+ dcsp->sd_ca_rexmt_int,
+ scsp_ca_retran_timeout);
+ (void)scsp_cfsm(dcsp, SCSP_CIFSM_CA_UPD,
+ (Scsp_msg *)0,
+ (Scsp_if_msg *)0);
+ rc = scsp_send_csus(dcsp);
+ }
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 6
+ * Retransmit timer expired -- retransmit last CA message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_06(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+
+ /*
+ * Resend the CA message
+ */
+ rc = scsp_send_msg(dcsp, dcsp->sd_ca_rexmt_msg);
+
+ /*
+ * Restart the retransmit timer
+ */
+ if (rc == 0) {
+ HARP_TIMER(&dcsp->sd_ca_rexmt_t, dcsp->sd_ca_rexmt_int,
+ scsp_ca_retran_timeout);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 7
+ * CSU Solicit received -- send it to the client interface FSM
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_07(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+ Scsp_msg *msg = (Scsp_msg *)p;
+
+ /*
+ * Cancel the CA retransmit timer and free any CA message
+ * saved for retransmission
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
+ }
+
+ /*
+ * Pass the CSUS to the client interface FSM
+ */
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CSU_SOL, msg,
+ (Scsp_if_msg *)0);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 8
+ * CSU Request received -- pass it to the client interface FSM
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_08(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+ Scsp_msg *msg = (Scsp_msg *)p;
+ Scsp_csa *csap;
+
+ /*
+ * Check whether this messages answers a CSUS
+ */
+ scsp_csus_ack(dcsp, msg);
+
+ /*
+ * If all CSAs requestd in CSUS messages have been
+ * received, the cache is aligned, so go to Aligned State
+ */
+ if (!dcsp->sd_csus_rexmt_msg && !dcsp->sd_crl &&
+ dcsp->sd_ca_state != SCSP_CAFSM_ALIGNED) {
+ dcsp->sd_ca_state = SCSP_CAFSM_ALIGNED;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CA_ALIGN,
+ (Scsp_msg *)0, (Scsp_if_msg *)0);
+ }
+
+ /*
+ * Pass the CSU Req to the client interface FSM
+ */
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_CSU_REQ, msg,
+ (Scsp_if_msg *)0);
+
+ /*
+ * Move the CSA chain from the message to the list of
+ * requests that need acknowledgements
+ */
+ for (csap = msg->sc_csu_msg->csu_csa_rec; csap;
+ csap = csap->next) {
+ LINK2TAIL(csap, Scsp_csa, dcsp->sd_csu_ack_pend, next);
+ }
+ msg->sc_csu_msg->csu_csa_rec = (Scsp_csa *)0;
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 9
+ * CA Retransmit timer expired in Update Cache or Aligned state--free
+ * the saved CA message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_09(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ /*
+ * Free any CA message saved for retransmission
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
+ }
+
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 10
+ * CSU Reply received -- Process the message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_10(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc = 0;
+ Scsp_msg *msg = (Scsp_msg *)p;
+ Scsp_csu_rexmt *rxp, *next_rxp;
+ Scsp_csa *csap, *next_csap, *mcp;
+
+ /*
+ * Dequeue acknowledged CSAs. For each CSAS in the received
+ * message, find the corresponding CSA on the CSU Request
+ * retransmit queue. Remove the CSA from the queue; if this
+ * results in the retransmit queue entry being empty, delete
+ * the entry. If the DCS has a newer CSA, send a CSUS to
+ * request it.
+ *
+ * Caution--potentially confusing lack of indentation ahead.
+ */
+ for (mcp = msg->sc_csu_msg->csu_csa_rec; mcp;
+ mcp = mcp->next) {
+ for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = next_rxp) {
+ next_rxp = rxp->sr_next;
+ for (csap = rxp->sr_csa; csap; csap = next_csap) {
+ next_csap = csap->next;
+ if (scsp_cmp_key(&csap->key, &mcp->key) ||
+ scsp_cmp_id(&csap->oid, &mcp->oid))
+ continue;
+ /*
+ * Found a CSA whose key and ID are equal to
+ * those in the CSU Reply
+ */
+ if (csap->seq == mcp->seq) {
+ /*
+ * The queued seq no is equal to the
+ * received seq no--the CSA is acknowledged
+ */
+ UNLINK(csap, Scsp_csa, rxp->sr_csa, next);
+ SCSP_FREE_CSA(csap);
+ } else if (csap->seq < mcp->seq) {
+ /*
+ * Queued seq no is less than received.
+ * We must dequeue the CSA and send a
+ * CSUS to request the more-up-to-date
+ * cache entry.
+ */
+ UNLINK(mcp, Scsp_csa,
+ msg->sc_csu_msg->csu_csa_rec,
+ next);
+ LINK2TAIL(mcp, Scsp_csa, dcsp->sd_crl, next);
+ UNLINK(csap, Scsp_csa, rxp->sr_csa, next);
+ SCSP_FREE_CSA(csap);
+ if (!dcsp->sd_csus_rexmt_msg) {
+ rc = scsp_send_csus(dcsp);
+ if (rc) {
+ return(rc);
+ }
+ }
+ }
+ /*
+ * Queued seq no is greater than
+ * received. Ignore the received CSAS.
+ */
+
+ /*
+ * If the retransmission block is empty, stop the
+ * timer and free it
+ */
+ if (!rxp->sr_csa) {
+ HARP_CANCEL(&rxp->sr_t);
+ UNLINK(rxp, Scsp_csu_rexmt,
+ dcsp->sd_csu_rexmt, sr_next);
+ free(rxp);
+ }
+
+ break;
+ } /* for (csap = ... */
+ } /* for (rxp = ... */
+ } /* for (mcp = ... */
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 11
+ * Updated cache entry -- update the summary cache and send a
+ * CSU Request
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to CSA describing new cache entry
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_11(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc, state;
+ Scsp_csa *csap = (Scsp_csa *)p;
+ Scsp_cse *csep;
+
+ /*
+ * Get the state of the CSA
+ */
+ switch(dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ state = csap->atmarp_data->sa_state;
+ break;
+ default:
+ SCSP_FREE_CSA(csap);
+ return(EINVAL);
+ }
+
+ if (state < SCSP_ASTATE_NEW || state > SCSP_ASTATE_DEL) {
+ SCSP_FREE_CSA(csap);
+ return(EINVAL);
+ }
+
+ /*
+ * Look up the cache summary entry for the CSA
+ */
+ SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);
+
+ /*
+ * Process ATMARP entries
+ */
+ if (dcsp->sd_server->ss_pid == SCSP_PROTO_ATMARP) {
+ switch(state) {
+ case SCSP_ASTATE_NEW:
+ case SCSP_ASTATE_UPD:
+ /*
+ * Add the entry if we don't have it already
+ */
+ if (!csep) {
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_ca_act_11: sizeof(Scsp_cse)");
+
+ csep->sc_key = csap->key;
+ SCSP_ADD(dcsp->sd_server, csep);
+ }
+
+ /*
+ * Update the cache summary entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_oid = csap->oid;
+ break;
+ case SCSP_ASTATE_DEL:
+ /*
+ * Delete any entry, but don't send the
+ * delete to the DCS
+ */
+ if (csep) {
+ SCSP_DELETE(dcsp->sd_server, csep);
+ free(csep);
+ }
+
+ SCSP_FREE_CSA(csap);
+ return(0);
+ }
+ }
+
+ /*
+ * Send the CSA in a CSU Request
+ */
+ csap->trans_ct = 0;
+ rc = scsp_send_csu_req(dcsp, csap);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 12
+ * CSUS retransmit timer expired--send a CSUS with any pending CSA
+ * records
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_12(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+
+ rc = scsp_send_csus(dcsp);
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 13
+ * CSU retransmit timer fired in Update or Aligned state--
+ * retransmit CSU Req
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to retransmission block whose timer fired
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_13(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc = 0;
+ Scsp_csu_rexmt *rxp = (Scsp_csu_rexmt *)p;
+ Scsp_csa *csap, *csap1, *next_csap;
+
+ /*
+ * Unlink and free the retransmit request block
+ */
+ csap = rxp->sr_csa;
+ UNLINK(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt, sr_next);
+ free(rxp);
+
+ /*
+ * Increment the transmission count for the CSAs in the request
+ */
+ for (csap1 = csap; csap1; csap1 = next_csap) {
+ next_csap = csap1->next;
+ csap1->trans_ct++;
+ if (csap1->trans_ct >= dcsp->sd_csu_rexmt_max) {
+ /*
+ * We've already sent this as many times as
+ * the limit allows. Drop this CSA.
+ */
+ UNLINK(csap1, Scsp_csa, csap, next);
+ SCSP_FREE_CSA(csap1);
+ }
+ }
+
+ /*
+ * Send another CSU Request with the CSA list, if it isn't
+ * empty now
+ */
+ if (csap) {
+ rc = scsp_send_csu_req(dcsp, csap);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 14
+ * Updated cache entry in Master/Slave Negotiation, Master, or
+ * Slave state--add entry to cache and CSA list
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to new cache summary entry
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_14(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ Scsp_csa *csap = (Scsp_csa *)p;
+ Scsp_cse *csep, *csep1;
+
+ /*
+ * Check to see whether we already have this
+ */
+ SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);
+
+ /*
+ * If we don't already have it and it's not being deleted,
+ * build a new cache summary entry
+ */
+ if (!csep && !csap->null) {
+ /*
+ * Get memory for a new entry
+ */
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_ca_act_14: sizeof(Scsp_cse)");
+
+ /*
+ * Fill out the new cache entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_key = csap->key;
+ csep->sc_oid = csap->oid;
+
+ /*
+ * Duplicate the new cache entry
+ */
+ csep1 = scsp_dup_cse(csep);
+
+ /*
+ * Add entry to the summary cache and the CSAS list
+ */
+ SCSP_ADD(dcsp->sd_server, csep);
+ LINK2TAIL(csep1, Scsp_cse, dcsp->sd_ca_csas, sc_next);
+ } else {
+ /*
+ * We already have the entry. Find it on the CSAS
+ * list.
+ */
+ for (csep1 = dcsp->sd_ca_csas; csep1;
+ csep1 = csep1->sc_next) {
+ if (scsp_cmp_key(&csep->sc_key,
+ &csep1->sc_key) == 0)
+ break;
+ }
+
+ /*
+ * Update or delete the entry
+ */
+ if (csap->null) {
+ /*
+ * The null flag is set--delete the entry
+ */
+ SCSP_DELETE(dcsp->sd_server, csep);
+ free(csep);
+ if (csep1) {
+ UNLINK(csep1, Scsp_cse,
+ dcsp->sd_ca_csas,
+ sc_next);
+ free(csep1);
+ }
+ } else {
+ /*
+ * Update the entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_oid = csap->oid;
+ if (!csep1) {
+ csep1 = scsp_dup_cse(csep);
+ LINK2TAIL(csep1, Scsp_cse,
+ dcsp->sd_ca_csas, sc_next);
+ } else {
+ csep1->sc_seq = csap->seq;
+ csep1->sc_oid = csap->oid;
+ }
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 15
+ * CA message received in Update Cache state--if we have a saved CA
+ * message, retransmit it; otherwise, go to Master/Slave Negotiation
+ * state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_15(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int rc;
+ Scsp_msg *msg = (Scsp_msg *)p;
+
+ /*
+ * If we don't have a saved CA message, or the sequence no. in
+ * the received message isn't right, fall back to Master/Slave
+ * Negotiation state
+ */
+ if (!dcsp->sd_ca_rexmt_msg ||
+ msg->sc_ca->ca_seq != dcsp->sd_ca_seq) {
+ dcsp->sd_ca_state = SCSP_CAFSM_NEG;
+ scsp_dcs_cleanup(dcsp);
+ rc = scsp_ca_act_01(dcsp, (Scsp_msg *)0);
+ } else {
+ /*
+ * Retransmit the saved CA message and reset the
+ * CA timer
+ */
+ rc = scsp_send_msg(dcsp, dcsp->sd_ca_rexmt_msg);
+ if (rc == 0) {
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ HARP_TIMER(&dcsp->sd_ca_rexmt_t,
+ dcsp->sd_ca_rexmt_int,
+ scsp_ca_retran_timeout);
+ }
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 16
+ * Update Response received from client in Update Cache or Aligned
+ * state. Move the acknowledged CSA to the acknowledged queue. If
+ * the list of CSAs pending acknowledgement is empty, send a CSU
+ * Reply.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to message from client
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_16(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ int found, rc = 0;
+ Scsp_if_msg *cmsg = (Scsp_if_msg *)p;
+ Scsp_csa *csap;
+
+ /*
+ * Find the acknowledged CSA
+ */
+ for (csap = dcsp->sd_csu_ack_pend, found = 0; csap && !found;
+ csap = csap->next) {
+ switch (dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ found = ((scsp_cmp_key(&csap->key,
+ &cmsg->si_atmarp.sa_key) == 0) &&
+ (scsp_cmp_id(&csap->oid,
+ &cmsg->si_atmarp.sa_oid) == 0));
+ break;
+ default:
+ /*
+ * Protocol not implemented
+ */
+ return(EPROTONOSUPPORT);
+ }
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ if (scsp_trace_mode & SCSP_TRACE_CAFSM) {
+ scsp_trace("scsp_ca_act_16: can't find CSA entry for Update Response\n");
+ }
+ return(0);
+ }
+
+ if (cmsg->si_rc == SCSP_RSP_OK) {
+ /*
+ * The server accepted the cache entry
+ */
+
+ /*
+ * Update SCSP's cache
+ */
+ scsp_update_cache(dcsp, csap);
+
+ /*
+ * Send this CSA to any other DCSs in the server group
+ */
+ rc = scsp_propagate_csa(dcsp, csap);
+ }
+
+ /*
+ * Move the CSA from the ACK pending queue to the
+ * acknowledged queue
+ */
+ UNLINK(csap, Scsp_csa, dcsp->sd_csu_ack_pend, next);
+ LINK2TAIL(csap, Scsp_csa, dcsp->sd_csu_ack, next);
+ if (!dcsp->sd_csu_ack_pend) {
+ /*
+ * ACK pending list is empty--send a CSU Reply
+ */
+ csap = dcsp->sd_csu_ack;
+ dcsp->sd_csu_ack = (Scsp_csa *)0;
+ rc = scsp_send_csu_reply(dcsp, csap);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * CA finite state machine action 17
+ * Ignore an event.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p ignored
+ *
+ * Returns:
+ * always returns 0
+ *
+ */
+int
+scsp_ca_act_17(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 18
+ * Updated cache entry in Down state--add entry to summary cache
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to new cache summary entry
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_18(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ Scsp_csa *csap = (Scsp_csa *)p;
+
+ /*
+ * Update the cache as appropriate
+ */
+ scsp_update_cache(dcsp, csap);
+
+ return(0);
+}
+
+
+/*
+ * CA finite state machine action 19
+ * Update Response received from client in Master/Slave Negotiation
+ * state. Update the cache as appropriate.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * p pointer to message from client
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_ca_act_19(dcsp, p)
+ Scsp_dcs *dcsp;
+ void *p;
+{
+ Scsp_if_msg *cmsg = (Scsp_if_msg *)p;
+ Scsp_csa *csap;
+
+ /*
+ * Ignore the message if the client rejected the update
+ */
+ if (cmsg->si_rc != SCSP_RSP_OK) {
+ return(0);
+ }
+
+ /*
+ * Create a CSAS from the client's update
+ */
+ csap = calloc(1, sizeof(Scsp_csa));
+ if (csap == NULL)
+ scsp_mem_err("scsp_ca_act_19: sizeof(Scsp_csa)");
+ csap->hops = 1;
+ switch (dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ csap->null = cmsg->si_atmarp.sa_state ==
+ SCSP_ASTATE_DEL;
+ csap->seq = cmsg->si_atmarp.sa_seq;
+ csap->key = cmsg->si_atmarp.sa_key;
+ csap->oid = cmsg->si_atmarp.sa_oid;
+ break;
+ default:
+ return(EINVAL);
+ }
+
+ /*
+ * Update SCSP's cache
+ */
+ scsp_update_cache(dcsp, csap);
+
+ return(0);
+}
diff --git a/usr.sbin/atm/scspd/scsp_config.c b/usr.sbin/atm/scspd/scsp_config.c
new file mode 100644
index 0000000..f692069
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config.c
@@ -0,0 +1,1158 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Configuration file processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+extern int yyparse(void);
+
+/*
+ * Global variables
+ */
+FILE *cfg_file;
+Scsp_server *current_server;
+Scsp_dcs *current_dcs;
+
+
+/*
+ * Process the configuration file
+ *
+ * This routine is called when the daemon starts, and it can also be
+ * called while it is running, as the result of a SIGHUP signal. It
+ * therefore has to be capable of both configuring the daemon from
+ * scratch and modifying the configuration of a running daemon.
+ *
+ * Arguments:
+ * cfn configuration file name
+ *
+ * Returns:
+ * 0 configuration read with no errors
+ * else error found in configuration file
+ *
+ */
+int
+scsp_config(cfn)
+ char *cfn;
+{
+ int rc;
+ Scsp_server *ssp, *snext;
+
+ /*
+ * Open the configuration file
+ */
+ cfg_file = fopen(cfn, "r");
+ if (!cfg_file) {
+ scsp_log(LOG_ERR, "can't open config file %s",
+ (void *)cfn);
+ exit(1);
+ }
+
+ /*
+ * Initialize current interface pointer
+ */
+ current_server = (Scsp_server *)0;
+
+ /*
+ * Clear marks on any existing servers
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ ssp->ss_mark = 0;
+ }
+
+ /*
+ * Scan the configuration file, processing each line as
+ * it is read
+ */
+ rc = yyparse();
+
+ /*
+ * Close the configuration file
+ */
+ fclose(cfg_file);
+
+ /*
+ * Delete any server entries that weren't updated
+ */
+ for (ssp = scsp_server_head; ssp; ssp = snext) {
+ snext = ssp->ss_next;
+ if (!ssp->ss_mark)
+ scsp_server_delete(ssp);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Prepare for SCSP DCS setup
+ *
+ * This routine is called from yyparse() when a DCS command is found.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+start_dcs()
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ /*
+ * Allocate a DCS block
+ */
+ dcsp = calloc(1, sizeof(Scsp_dcs));
+ if (dcsp == NULL)
+ scsp_mem_err("start_dcs: sizeof(Scsp_dcs)");
+
+ /*
+ * Fill out DCS links and default values
+ */
+ dcsp->sd_server = current_server;
+ dcsp->sd_addr.address_format = T_ATM_ABSENT;
+ dcsp->sd_subaddr.address_format = T_ATM_ABSENT;
+ dcsp->sd_sock = -1;
+ dcsp->sd_ca_rexmt_int = SCSP_CAReXmitInterval;
+ dcsp->sd_csus_rexmt_int = SCSP_CSUSReXmitInterval;
+ dcsp->sd_hops = SCSP_CSA_HOP_CNT;
+ dcsp->sd_csu_rexmt_int = SCSP_CSUReXmitInterval;
+ dcsp->sd_csu_rexmt_max = SCSP_CSUReXmitMax;
+ LINK2TAIL(dcsp, Scsp_dcs, current_server->ss_dcs, sd_next);
+
+ current_dcs = dcsp;
+ return(0);
+}
+
+
+/*
+ * Finish up server configuration
+ *
+ * This routine is called from yyparse() to at the end of a DCS
+ * command. It checks that required fields are set and finishes
+ * up the DCS block.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+finish_dcs()
+{
+ int rc = 0;
+ Scsp_dcs *dcsp;
+ Scsp_server *ssp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ ssp = current_server;
+ dcsp = current_dcs;
+
+ /*
+ * Make sure the DCS ID is set
+ */
+ if (dcsp->sd_dcsid.id_len == 0) {
+ parse_error("DCS ID not set");
+ rc++;
+ }
+
+ /*
+ * Make sure the ATM address is set
+ */
+ if (dcsp->sd_addr.address_format == T_ATM_ABSENT) {
+ parse_error("DCS ATM address not set");
+ rc++;
+ }
+
+ current_dcs = (Scsp_dcs *)0;
+ return(rc);
+}
+
+
+/*
+ * Configure DCS ATM address
+ *
+ * This routine is called from yyparse() to process an ATMaddr command.
+ *
+ * Arguments:
+ * ap pointer to DCS's ATM address (in ASCII)
+ * sap pointer to DCS's ATM subaddress (in ASCII)
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_addr(ap, sap)
+ char *ap, *sap;
+{
+ Scsp_dcs *dcsp;
+ Atm_addr addr, subaddr;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+ /*
+ * Initialize
+ */
+ bzero(&addr, sizeof(addr));
+ addr.address_format = T_ATM_ABSENT;
+ bzero(&subaddr, sizeof(subaddr));
+ subaddr.address_format = T_ATM_ABSENT;
+
+ /*
+ * Convert the ATM address from character to internal format
+ */
+ if (ap) {
+ addr.address_length = get_hex_atm_addr(ap,
+ (u_char *)addr.address, strlen(ap));
+ if (addr.address_length == 0) {
+ parse_error("invalid ATM address");
+ return(1);
+ }
+ if (addr.address_length == sizeof(Atm_addr_nsap)) {
+ addr.address_format = T_ATM_ENDSYS_ADDR;
+ } else if (addr.address_length <=
+ sizeof(Atm_addr_e164)) {
+ addr.address_format = T_ATM_E164_ADDR;
+ } else {
+ parse_error("invalid ATM address");
+ return(1);
+ }
+ }
+
+ /*
+ * Convert the ATM subaddress from character to internal format
+ */
+ if (sap) {
+ subaddr.address_length = get_hex_atm_addr(sap,
+ (u_char *)subaddr.address, strlen(sap));
+ if (subaddr.address_length == 0) {
+ parse_error("invalid ATM address");
+ return(1);
+ }
+ if (subaddr.address_length == sizeof(Atm_addr_nsap)) {
+ subaddr.address_format = T_ATM_ENDSYS_ADDR;
+ } else if (subaddr.address_length <=
+ sizeof(Atm_addr_e164)) {
+ subaddr.address_format = T_ATM_E164_ADDR;
+ } else {
+ parse_error("invalid ATM subaddress");
+ return(1);
+ }
+ }
+
+ /*
+ * Make sure we have a legal ATM address type combination
+ */
+ if (((addr.address_format != T_ATM_ENDSYS_ADDR) ||
+ (subaddr.address_format != T_ATM_ABSENT)) &&
+ ((addr.address_format != T_ATM_E164_ADDR) ||
+ (subaddr.address_format != T_ATM_ENDSYS_ADDR))) {
+ parse_error("invalid address/subaddress combination");
+ return(1);
+ }
+
+ /*
+ * Save the address and subaddress
+ */
+ ATM_ADDR_COPY(&addr, &dcsp->sd_addr);
+ ATM_ADDR_COPY(&subaddr, &dcsp->sd_subaddr);
+
+ return(0);
+}
+
+
+/*
+ * Configure CA retransmit interval for DCS
+ *
+ * This routine is called from yyparse() to process a CAReXmitInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_ca_rexmit(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CA retransmit interval");
+ return(1);
+ }
+
+ /*
+ * Set CA retransmit interval
+ */
+ dcsp->sd_ca_rexmt_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure CSUS retransmit interval for DCS
+ *
+ * This routine is called from yyparse() to process a CSUSReXmitInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_csus_rexmit(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CSUS retransmit interval");
+ return(1);
+ }
+
+ /*
+ * Set CSUS retransmit interval
+ */
+ dcsp->sd_csus_rexmt_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure CSU retransmit interval for DCS
+ *
+ * This routine is called from yyparse() to process a CSUReXmitInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_csu_rexmit(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CSU retransmit interval");
+ return(1);
+ }
+
+ /*
+ * Set CSU retransmit interval
+ */
+ dcsp->sd_csu_rexmt_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure CSU retransmit limit for DCS
+ *
+ * This routine is called from yyparse() to process a CSUReXmitMax
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_csu_rexmit_max(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid CSU retransmit maximum");
+ return(1);
+ }
+
+ /*
+ * Set CSU retransmit limit
+ */
+ dcsp->sd_csu_rexmt_max = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure Hello dead factor for DCS
+ *
+ * This routine is called from yyparse() to process a HelloDead
+ * command.
+ *
+ * Arguments:
+ * val number of times Hello interval has to expire before
+ * a DCS is considered dead
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_hello_df(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the limit
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid Hello dead factor");
+ return(1);
+ }
+
+ /*
+ * Set Hello dead factor
+ */
+ dcsp->sd_hello_df = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure Hello interval for DCS
+ *
+ * This routine is called from yyparse() to process a HelloInt
+ * command.
+ *
+ * Arguments:
+ * val time interval
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_hello_int(val)
+ int val;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the interval
+ */
+ if (val <= 0 || val > 1024) {
+ parse_error("invalid Hello interval");
+ return(1);
+ }
+
+ /*
+ * Set Hello interval
+ */
+ dcsp->sd_hello_int = val;
+
+ return(0);
+}
+
+
+/*
+ * Configure hop count for SCSP server
+ *
+ * This routine is called from yyparse() to process a Hops command.
+ *
+ * Arguments:
+ * hops number of hops
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_hops(hops)
+ int hops;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ dcsp = current_dcs;
+
+
+ /*
+ * Validate the count
+ */
+ if (hops <= 0 || hops > 1024) {
+ parse_error("invalid hop count");
+ return(1);
+ }
+
+ /*
+ * Set hop count
+ */
+ dcsp->sd_hops = hops;
+
+ return(0);
+}
+
+
+/*
+ * Configure DCS ID
+ *
+ * This routine is called from yyparse() to process an ID command.
+ *
+ * Arguments:
+ * name pointer to DCS's DNS name or IP address (in ASCII)
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_dcs_id(name)
+ char *name;
+{
+ Scsp_dcs *dcsp;
+ Scsp_server *ssp;
+ struct sockaddr_in *ip_addr;
+
+ /*
+ * Make sure we have a current server block and DCS block
+ */
+ if (!current_server) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ if (!current_dcs) {
+ parse_error("server not found");
+ return(1);
+ }
+ ssp = current_server;
+ dcsp = current_dcs;
+
+ /*
+ * Convert the DNS name or IP address
+ */
+ ip_addr = get_ip_addr(name);
+ if (!ip_addr) {
+ parse_error("invalid DCS IP address");
+ return(1);
+ }
+
+ /*
+ * Verify the address length
+ */
+ if (ssp->ss_id_len != sizeof(ip_addr->sin_addr)) {
+ parse_error("invalid DCS ID length");
+ return(1);
+ }
+
+ /*
+ * Set the ID in the DCS block
+ */
+ dcsp->sd_dcsid.id_len = ssp->ss_id_len;
+ bcopy(&ip_addr->sin_addr, dcsp->sd_dcsid.id, ssp->ss_id_len);
+
+ return(0);
+}
+
+
+/*
+ * Configure network interface for SCSP server
+ *
+ * This routine is called from yyparse() to process a Netif command.
+ * It verifies the network interface name, gets interface information
+ * from the kernel, and sets the appropriate fields in the server
+ * control block.
+ *
+ * Arguments:
+ * netif pointer to network interface name
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_intf(netif)
+ char *netif;
+{
+ int rc;
+ Scsp_server *ssp;
+
+ /*
+ * Get the current network interface address
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("Server not found");
+ rc = 1;
+ goto set_intf_done;
+ }
+
+ /*
+ * Make sure we're configuring a valid
+ * network interface
+ */
+ rc = verify_nif_name(netif);
+ if (rc == 0) {
+ parse_error("%s is not a valid network interface",
+ (void *)netif);
+ rc = 1;
+ goto set_intf_done;
+ } else if (rc < 0) {
+ scsp_log(LOG_ERR, "Netif name verify error");
+ exit(1);
+ }
+
+ /*
+ * Save the server's network interface name
+ */
+ strcpy(ssp->ss_intf, netif);
+ rc = 0;
+
+set_intf_done:
+ return(rc);
+}
+
+
+/*
+ * Configure protocol for SCSP server
+ *
+ * This routine is called from yyparse() to process a Protocol command.
+ *
+ * Arguments:
+ * proto SCSP protocol being configured
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_protocol(proto)
+ int proto;
+{
+ Scsp_server *ssp;
+
+ /*
+ * Get address of current server block
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ /*
+ * Process based on protocol ID
+ */
+ switch(proto) {
+ case SCSP_PROTO_ATMARP:
+ ssp->ss_pid = proto;
+ ssp->ss_id_len = SCSP_ATMARP_ID_LEN;
+ ssp->ss_ckey_len = SCSP_ATMARP_KEY_LEN;
+ break;
+ case SCSP_PROTO_NHRP:
+ ssp->ss_pid = proto;
+ ssp->ss_id_len = SCSP_NHRP_ID_LEN;
+ ssp->ss_ckey_len = SCSP_NHRP_KEY_LEN;
+ break;
+ case SCSP_PROTO_MARS:
+ case SCSP_PROTO_DHCP:
+ case SCSP_PROTO_LNNI:
+ default:
+ parse_error("invalid protocol");
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * Configure server group for SCSP server
+ *
+ * This routine is called from yyparse() to process a ServerGroupID
+ * command.
+ *
+ * Arguments:
+ * sgid server group id
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_server_group(sgid)
+ int sgid;
+{
+ Scsp_server *ssp;
+
+ /*
+ * Get address of current server block
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("server not found");
+ return(1);
+ }
+
+ /*
+ * Validate server group ID
+ */
+ if (sgid <= 0) {
+ parse_error("invalid server group ID");
+ return(1);
+ }
+
+ /*
+ * Save the ID
+ */
+ ssp->ss_sgid = sgid;
+
+ return(0);
+}
+
+
+/*
+ * Prepare for SCSP server setup
+ *
+ * This routine is called from yyparse() when a Server statment is
+ * found.
+ *
+ * Arguments:
+ * name pointer to LIS name
+ *
+ * Returns:
+ * 0 success
+ * else error encountered
+ *
+ */
+int
+start_server(name)
+ char *name;
+{
+ int i;
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp, *next_dcs;
+ Scsp_cse *csep, *next_cse;
+
+ /*
+ * See if we already have an entry for this name
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (strcasecmp(ssp->ss_name, name) == 0)
+ break;
+ }
+
+ if (ssp) {
+ /*
+ * Log the fact that we're updating the entry
+ */
+ scsp_log(LOG_INFO, "updating server entry for %s",
+ (void *)name);
+
+ /*
+ * Free the existing cache
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (csep = ssp->ss_cache[i]; csep;
+ csep = next_cse) {
+ next_cse = csep->sc_next;
+ UNLINK(csep, Scsp_cse, ssp->ss_cache[i],
+ sc_next);
+ free(csep);
+ }
+ }
+
+ /*
+ * Delete existing DCS blocks
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = next_dcs) {
+ next_dcs = dcsp->sd_next;
+ scsp_dcs_delete(dcsp);
+ }
+ } else {
+ /*
+ * Get a new server entry
+ */
+ ssp = calloc(1, sizeof(Scsp_server));
+ if (ssp == NULL) {
+ scsp_log(LOG_ERR, "unable to allocate server entry");
+ exit(1);
+ }
+ ssp->ss_sock = -1;
+ ssp->ss_dcs_lsock = -1;
+
+ /*
+ * Set the name
+ */
+ ssp->ss_name = strdup(name);
+
+ /*
+ * Link in the new interface entry
+ */
+ LINK2TAIL(ssp, Scsp_server, scsp_server_head,
+ ss_next);
+ }
+
+ /*
+ * If the mark is already set, this is a duplicate command
+ */
+ if (ssp->ss_mark) {
+ parse_error("duplicate server \"%s\"", name);
+ return(1);
+ }
+
+ /*
+ * Make this the current interface
+ */
+ current_server = ssp;
+
+ return(0);
+}
+
+
+/*
+ * Finish up server configuration
+ *
+ * This routine is called from yyparse() when the end of a server
+ * statement is reached. It checks that required fields are set
+ * and marks the entry as processed.
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * 0 OK
+ * 1 Error
+ *
+ */
+int
+finish_server()
+{
+ int rc = 0;
+ Scsp_server *ssp;
+
+ /*
+ * Get the current network interface address
+ */
+ ssp = current_server;
+ if (!ssp) {
+ parse_error("Server not found");
+ rc++;
+ }
+
+ /*
+ * Mark the interface as processed
+ */
+ ssp->ss_mark = 1;
+
+ /*
+ * Make sure the interface has been configured
+ */
+ if (ssp->ss_intf == (char *)0) {
+ parse_error("netif missing from server specification");
+ rc++;
+ }
+
+ /*
+ * Make sure the protocol is set
+ */
+ if (ssp->ss_pid == 0) {
+ parse_error("protocol missing from server specification");
+ rc++;
+ }
+
+ /*
+ * Make sure the server group is set
+ */
+ if (ssp->ss_sgid == 0) {
+ parse_error("server group ID missing from server specification");
+ rc++;
+ }
+
+ /*
+ * Make sure at least one DCS is configured
+ */
+ if (ssp->ss_dcs == (Scsp_dcs *)0) {
+ parse_error("no DCS configured for server");
+ rc++;
+ }
+
+ /*
+ * Mark the end of the server
+ */
+ current_server = (Scsp_server *)0;
+
+ return(rc);
+}
+
+
+/*
+ * Configure log file for SCSP server
+ *
+ * This routine is called from yyparse() to process a log File command.
+ *
+ * Arguments:
+ * file name of logging file
+ *
+ * Returns:
+ * 0 success
+ * 1 error encountered
+ *
+ */
+int
+set_log_file(file)
+ char *file;
+{
+ /*
+ * Make sure we haven't already got a log file
+ */
+ if (scsp_log_file) {
+ parse_error("multiple log files specified");
+ return(1);
+ }
+
+ /*
+ * Open the file
+ */
+ scsp_log_file = fopen(file, "a");
+ if (!scsp_log_file) {
+ parse_error("can't open log file");
+ return(1);
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/atm/scspd/scsp_config_lex.c b/usr.sbin/atm/scspd/scsp_config_lex.c
new file mode 100644
index 0000000..20002b2
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config_lex.c
@@ -0,0 +1,530 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Parse a configuration file into tokens
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+#include "scsp_config_parse.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+int parse_line = 1;
+
+/*
+ * Local definitions
+ */
+#define TOK_MAX_LEN 128
+
+/*
+ * Character classes
+ */
+#define CHAR_INVALID 0 /* Not allowed */
+#define CHAR_ALPHA 1 /* G-W, Y, Z */
+#define CHAR_HEX_DIGIT 2 /* A-F */
+#define CHAR_X 3 /* X */
+#define CHAR_0 4 /* '0' */
+#define CHAR_DIGIT 5 /* 1-9 */
+#define CHAR_SPACE 6 /* space, tab */
+#define CHAR_DECIMAL 7 /* period */
+#define CHAR_SLASH 8 /* slash */
+#define CHAR_ASTERISK 9 /* asterisk */
+#define CHAR_HASH 10 /* pound sign */
+#define CHAR_SPECIAL 11 /* semicolon, braces */
+#define CHAR_MISC 12 /* chars allowd in file names */
+#define CHAR_EOL 13 /* new line */
+#define CHAR_EOF 14 /* EOF */
+#define CHAR_CNT CHAR_EOF + 1
+
+/*
+ * Character class table (initialized by init_class_tbl())
+ */
+static char class_tbl[128];
+
+/*
+ * State table element structure
+ */
+struct state_entry {
+ int action;
+ int next;
+};
+
+/*
+ * Scanner states
+ */
+#define TS_INIT 0
+#define TS_ALPHA 1
+#define TS_INT_1 2
+#define TS_INT 3
+#define TS_HEX 4
+#define TS_SLASH_1 5
+#define TS_COMMENT 6
+#define TS_COMMENT_1 7
+#define TS_FLUSH 8
+#define TS_HEX_1 9
+#define TS_CNT TS_HEX_1 + 1
+
+/*
+ * Token scanner state table
+ */
+static struct state_entry token_state_tbl[CHAR_CNT][TS_CNT] = {
+/* 0 1 2 3 4 5 6 7 8 9 */
+/* bad */{{2,0},{2,0},{2,0},{2,0},{2,0},{2,0},{0,6},{0,6},{0,8},{2,0}},
+/* g-z */{{1,1},{1,1},{1,1},{1,1},{2,0},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* a-f */{{1,1},{1,1},{1,1},{1,1},{1,9},{1,1},{0,6},{0,6},{0,8},{1,4}},
+/* x */{{1,1},{1,1},{1,4},{1,4},{2,0},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* 0 */{{1,2},{1,1},{1,3},{1,3},{1,9},{1,1},{0,6},{0,6},{0,8},{1,4}},
+/* 1-9 */{{1,3},{1,1},{1,3},{1,3},{1,9},{1,1},{0,6},{0,6},{0,8},{1,4}},
+/* sp */{{0,0},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,8},{2,0}},
+/* . */{{2,0},{1,1},{1,1},{1,1},{1,4},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* / */{{1,5},{1,1},{1,1},{1,1},{7,0},{4,8},{0,6},{0,0},{0,8},{2,0}},
+/* * */{{2,0},{6,0},{8,0},{8,0},{7,0},{4,6},{0,7},{0,7},{0,8},{2,0}},
+/* # */{{0,8},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,8},{2,0}},
+/* ;{} */{{3,0},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,8},{2,0}},
+/* Msc */{{2,0},{1,1},{1,1},{1,1},{2,0},{1,1},{0,6},{0,6},{0,8},{2,0}},
+/* EOL */{{0,0},{6,0},{8,0},{8,0},{7,0},{6,0},{0,6},{0,6},{0,0},{2,0}},
+/* EOF */{{9,0},{6,0},{8,0},{8,0},{7,0},{6,0},{2,0},{2,0},{9,0},{2,0}},
+};
+
+
+/*
+ * Reserved words
+ */
+static struct {
+ char *word;
+ int token;
+} rsvd_word_tbl[] = {
+ { "ATMaddr", TOK_DCS_ADDR },
+ { "ATMARP", TOK_ATMARP },
+ { "CAReXmitInt", TOK_DCS_CA_REXMIT_INT },
+ { "CSUSReXmitInt", TOK_DCS_CSUS_REXMIT_INT },
+ { "CSUReXmitInt", TOK_DCS_CSU_REXMIT_INT },
+ { "CSUReXmitMax", TOK_DCS_CSU_REXMIT_MAX },
+ { "DCS", TOK_DCS },
+ { "DHCP", TOK_DHCP },
+ { "familyID", TOK_FAMILY },
+ { "file", TOK_LFN },
+ { "hops", TOK_DCS_HOP_CNT },
+ { "HelloDead", TOK_DCS_HELLO_DF },
+ { "HelloInt", TOK_DCS_HELLO_INT },
+ { "ID", TOK_DCS_ID },
+ { "LNNI", TOK_LNNI },
+ { "log", TOK_LOG },
+ { "MARS", TOK_MARS },
+ { "netif", TOK_NETIF },
+ { "NHRP", TOK_NHRP },
+ { "protocol", TOK_PROTOCOL },
+ { "server", TOK_SERVER },
+ { "ServerGroupID", TOK_SRVGRP },
+ { "syslog", TOK_SYSLOG },
+ { (char *)0, 0 },
+};
+
+
+/*
+ * Copy a character string
+ *
+ * Make a copy of a character string, using strdup. If strdup fails,
+ * meaning we're out of memory, then print an error message and exit.
+ *
+ * Arguments:
+ * s string to be copied
+ *
+ * Returns:
+ * char * pointer to area provided by strdup
+ *
+ */
+static char *
+copy_buffer(s)
+ char *s;
+{
+ char *t;
+
+ t = strdup(s);
+
+ if (!t) {
+ fprintf(stderr, "%s: strdup failed\n", prog);
+ exit(1);
+ }
+
+ return(t);
+}
+
+
+/*
+ * Push a character back onto the input stream.
+ *
+ * Arguments:
+ * c character to be pushed
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+push_char(c)
+ char c;
+{
+ if (c == '\n')
+ parse_line--;
+
+ ungetc(c, cfg_file);
+}
+
+
+/*
+ * Initialize the character class table.
+ *
+ * Set each entry in the character class table to the class
+ * corresponding to the character.
+ *
+ * Arguments:
+ * tbl pointer to table to be initialized
+ *
+ * Returns:
+ * None
+ */
+static void
+init_class_tbl(tbl)
+ char *tbl;
+{
+ int i;
+ char c;
+
+ /*
+ * Set up the table for all ASCII characters
+ */
+ for (i=0; isascii((char)i); i++) {
+ /*
+ * Clear entry
+ */
+ tbl[i] = CHAR_INVALID;
+
+ /*
+ * Set entries depending on character type
+ */
+ c = (char)i;
+ if (c == 'a' || c == 'b' || c == 'c' ||
+ c == 'd' || c == 'e' || c == 'f' ||
+ c == 'A' || c == 'B' || c == 'C' ||
+ c == 'D' || c == 'E' || c == 'F')
+ tbl[i] = CHAR_HEX_DIGIT;
+ else if (c == 'x' || c == 'X')
+ tbl[i] = CHAR_X;
+ else if (isalpha(c))
+ tbl[i] = CHAR_ALPHA;
+ else if (c == '0')
+ tbl[i] = CHAR_0;
+ else if (isdigit(c))
+ tbl[i] = CHAR_DIGIT;
+ else if (c == '\n')
+ tbl[i] = CHAR_EOL;
+ else if (c == ' ' || c == '\t')
+ tbl[i] = CHAR_SPACE;
+ else if (c == '#')
+ tbl[i] = CHAR_HASH;
+ else if (c == '*')
+ tbl[i] = CHAR_ASTERISK;
+ else if (c == '.')
+ tbl[i] = CHAR_DECIMAL;
+ else if (c == '/')
+ tbl[i] = CHAR_SLASH;
+ else if (c == ';' || c == '{' || c == '}')
+ tbl[i] = CHAR_SPECIAL;
+ else if (c == '-' || c == '_' || c == '&' || c == '@' ||
+ c == '~')
+ tbl[i] = CHAR_MISC;
+ }
+}
+
+
+/*
+ * Get the class of a character.
+ *
+ * Arguments:
+ * c character being scanned
+ *
+ * Returns:
+ * int character class
+ */
+static int
+char_class(c)
+ char c;
+{
+ int class = CHAR_INVALID;
+
+ if (c == EOF) {
+ class = CHAR_EOF;
+ } else if (c < 0 || !isascii(c)) {
+ class = CHAR_INVALID;
+ } else {
+ class = class_tbl[(int)c];
+ }
+
+ return(class);
+}
+
+
+/*
+ * Print an error message when the scanner finds an error
+ *
+ * Arguments:
+ * c character on which the error was recognized
+ * state scanner state at error
+ *
+ * Returns:
+ * None
+ */
+static void
+scan_error(c, state)
+ char c;
+ int state;
+{
+ /*
+ * Check for invalid character
+ */
+ if (char_class(c) == CHAR_INVALID) {
+ parse_error("Invalid character 0x%x encountered",
+ c);
+ return;
+ }
+
+ /*
+ * Check for unexpected EOF
+ */
+ if (char_class(c) == CHAR_EOF) {
+ parse_error("Unexpected end of file");
+ return;
+ }
+
+ /*
+ * Error depends on state
+ */
+ switch(state) {
+ case TS_INIT:
+ parse_error("Syntax error at '%c'", c);
+ break;
+ case TS_ALPHA:
+ case TS_INT_1:
+ case TS_INT:
+ case TS_SLASH_1:
+ case TS_COMMENT:
+ case TS_COMMENT_1:
+ case TS_FLUSH:
+ parse_error("Syntax error");
+ break;
+ case TS_HEX:
+ case TS_HEX_1:
+ parse_error("Syntax error in hex string");
+ break;
+ }
+}
+
+
+/*
+ * Assemble a token
+ *
+ * Read a character at a time from the input file, assembling the
+ * characters into tokens as specified by the token scanner state
+ * table. Return the completed token.
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * token the type of the token found
+ */
+int
+yylex()
+{
+ int i, state;
+ char c, token_buffer[TOK_MAX_LEN];
+
+ /*
+ * Initialize
+ */
+ if (class_tbl['A'] != CHAR_HEX_DIGIT)
+ init_class_tbl(class_tbl);
+ state = TS_INIT;
+ bzero(token_buffer, sizeof(token_buffer));
+ bzero(&yylval, sizeof(yylval));
+
+ /*
+ * Handle a character at a time until a token is built
+ */
+ while(1) {
+ /*
+ * Read a character from the input file.
+ */
+ c = (char)getc(cfg_file);
+ if (c == '\n') {
+ parse_line++;
+ }
+
+#ifdef NOTDEF
+ printf("token_state: state=%d, char=%c, class=%d, action=%d, next=%d\n",
+ state,
+ c,
+ char_class(c),
+ token_state_tbl[char_class][state].action,
+ token_state_tbl[char_class][state].next);
+#endif
+
+ /*
+ * Perform an action based on the state table
+ */
+ switch(token_state_tbl[char_class(c)][state].action) {
+ case 0:
+ /*
+ * Ignore the character
+ */
+ break;
+ case 1:
+ /*
+ * Add character to buffer
+ */
+ if (strlen(token_buffer) < TOK_MAX_LEN) {
+ token_buffer[strlen(token_buffer)] = c;
+ }
+ break;
+ case 2:
+ /*
+ * Error--print a message and start over
+ */
+ scan_error(c, state);
+ break;
+ case 3:
+ /*
+ * Return special character
+ */
+ return(c);
+ break;
+ case 4:
+ /*
+ * Clear the token buffer
+ */
+ bzero(token_buffer, sizeof(token_buffer));
+ break;
+ case 5:
+ /*
+ * Not used
+ */
+ break;
+ case 6:
+ /*
+ * Return character token
+ */
+ push_char(c);
+
+ /*
+ * Check for reserved words
+ */
+ for (i=0; rsvd_word_tbl[i].word; i++) {
+ if (strcasecmp(token_buffer,
+ rsvd_word_tbl[i].word) == 0)
+ break;
+ }
+ if (rsvd_word_tbl[i].word) {
+ return(rsvd_word_tbl[i].token);
+ }
+
+ /*
+ * Word isn't reserved, return alpha string
+ */
+ yylval.tv_alpha = copy_buffer(token_buffer);
+ return(TOK_NAME);
+ break;
+ case 7:
+ /*
+ * Return hex string (ATM address)
+ */
+ push_char(c);
+ yylval.tv_hex = copy_buffer(token_buffer);
+ return(TOK_HEX);
+ break;
+ case 8:
+ /*
+ * Return integer
+ */
+ push_char(c);
+ yylval.tv_int = atoi(token_buffer);
+ return(TOK_INTEGER);
+ break;
+ case 9:
+ /*
+ * Return EOF
+ */
+ return(0);
+ break;
+ default:
+ fprintf(stderr, "Invalid action indicator, state=%d, char=0x%02x\n",
+ state, c);
+ break;
+ }
+
+ /*
+ * Set the next state and bump to the next character
+ */
+ state = token_state_tbl[char_class(c)][state].next;
+ }
+}
diff --git a/usr.sbin/atm/scspd/scsp_config_parse.y b/usr.sbin/atm/scspd/scsp_config_parse.y
new file mode 100644
index 0000000..b5c34b9
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config_parse.y
@@ -0,0 +1,412 @@
+%{
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * YACC input for configuration file processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <libatm.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+void yyerror(char *);
+%}
+
+
+/*
+ * Token value definition
+ */
+%union {
+ char *tv_alpha;
+ int tv_int;
+ char *tv_hex;
+}
+
+
+/*
+ * Token types returned by scanner
+ */
+%token <tv_alpha> TOK_NAME
+%token <tv_int> TOK_INTEGER
+%token <tv_hex> TOK_HEX
+
+/*
+ * Reserved words
+ */
+%token TOK_ATMARP
+%token TOK_DCS
+%token TOK_DCS_ADDR
+%token TOK_DCS_CA_REXMIT_INT
+%token TOK_DCS_CSUS_REXMIT_INT
+%token TOK_DCS_CSU_REXMIT_INT
+%token TOK_DCS_CSU_REXMIT_MAX
+%token TOK_DCS_HELLO_DF
+%token TOK_DCS_HELLO_INT
+%token TOK_DCS_HOP_CNT
+%token TOK_DCS_ID
+%token TOK_DHCP
+%token TOK_FAMILY
+%token TOK_LFN
+%token TOK_LNNI
+%token TOK_LOG
+%token TOK_MARS
+%token TOK_NETIF
+%token TOK_NHRP
+%token TOK_PROTOCOL
+%token TOK_SERVER
+%token TOK_SRVGRP
+%token TOK_SYSLOG
+
+
+%%
+cfg_file: /* Empty */
+ | stmt_seq
+
+stmt_seq: stmt
+ | stmt_seq stmt
+ ;
+
+stmt: server_stmt ';'
+ | log_stmt ';'
+ ;
+
+/*
+ * SCSP server definition statements
+ */
+server_stmt: TOK_SERVER TOK_NAME
+ {
+ int rc;
+
+ rc = start_server($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ '{' server_def '}'
+ {
+ int rc;
+
+ rc = finish_server();
+ if (rc)
+ return(rc);
+ }
+ ;
+
+server_def: server_spec ';'
+ | server_def server_spec ';'
+ ;
+
+server_spec: /* Nothing */
+ | dcs_stmt
+ | TOK_NETIF TOK_NAME
+ {
+ int rc;
+
+ /*
+ * Configure the network interface
+ */
+ rc = set_intf($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_PROTOCOL TOK_ATMARP
+ {
+ int rc;
+
+ /*
+ * Configure the protocol
+ */
+ rc = set_protocol(SCSP_PROTO_ATMARP);
+ if (rc)
+ return(rc);
+ }
+ | TOK_PROTOCOL TOK_DHCP | TOK_LNNI | TOK_MARS | TOK_NHRP
+ {
+ yyerror("Protocol not implemented");
+ return(1);
+ }
+ | TOK_SRVGRP TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the SCSP server group ID
+ */
+ rc = set_server_group($2);
+ if (rc)
+ return(rc);
+ }
+ ;
+
+/*
+ * SCSP DCS definition statements
+ */
+dcs_stmt: TOK_DCS
+ {
+ int rc;
+
+ rc = start_dcs();
+ if (rc)
+ return(rc);
+ }
+ '{' dcs_def '}'
+ {
+ int rc;
+
+ rc = finish_dcs();
+ if (rc)
+ return(rc);
+ }
+ ;
+
+dcs_def: dcs_spec ';'
+ | dcs_def dcs_spec ';'
+ ;
+
+dcs_spec: /* Nothing */
+ | TOK_DCS_ADDR TOK_HEX
+ {
+ int rc;
+
+ /*
+ * Set DCS address
+ */
+ rc = set_dcs_addr($2, (char *)0);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_ADDR TOK_HEX TOK_HEX
+ {
+ int rc;
+
+ /*
+ * Set DCS address and subaddress
+ */
+ rc = set_dcs_addr($2, $3);
+ free($2);
+ free($3);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CA_REXMIT_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CA retransmit interval
+ */
+ rc = set_dcs_ca_rexmit($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CSUS_REXMIT_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CSUS retransmit interval
+ */
+ rc = set_dcs_csus_rexmit($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CSU_REXMIT_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CSU retransmit interval
+ */
+ rc = set_dcs_csu_rexmit($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_CSU_REXMIT_MAX TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the CSU retransmit limit
+ */
+ rc = set_dcs_csu_rexmit_max($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_HELLO_DF TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the Hello dead factor
+ */
+ rc = set_dcs_hello_df($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_HELLO_INT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the Hello interval
+ */
+ rc = set_dcs_hello_int($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_HOP_CNT TOK_INTEGER
+ {
+ int rc;
+
+ /*
+ * Configure the hop count
+ */
+ rc = set_dcs_hops($2);
+ if (rc)
+ return(rc);
+ }
+ | TOK_DCS_ID TOK_NAME
+ {
+ int rc;
+
+ /*
+ * Configure the DCS ID
+ */
+ rc = set_dcs_id($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ ;
+
+
+/*
+ * Logging option statements
+ */
+log_stmt: TOK_LOG
+ '{' log_spec '}'
+ ;
+
+log_spec: /* Nothing */
+ | TOK_LFN TOK_NAME ';'
+ {
+ /*
+ * Configure the log file name
+ */
+ int rc;
+
+ rc = set_log_file($2);
+ free($2);
+ if (rc)
+ return(rc);
+ }
+ ;
+ | TOK_SYSLOG ';'
+ {
+ /*
+ * Configure logging to syslog
+ */
+ scsp_log_syslog = 1;
+ }
+ ;
+
+%%
+
+void
+#if __STDC__
+parse_error(const char *fmt, ...)
+#else
+parse_error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buff[256];
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ vsprintf(buff, fmt, ap);
+ scsp_log(LOG_ERR, "%s: Config file error at line %d: %s\n",
+ prog, parse_line, buff);
+#ifdef NOTDEF
+ fprintf(stderr, "%s: Config file error at line %d: %s\n",
+ prog, parse_line, buff);
+#endif
+ va_end(ap);
+}
+
+
+void
+yyerror(s)
+ char *s;
+{
+ parse_error(s);
+}
diff --git a/usr.sbin/atm/scspd/scsp_hfsm.c b/usr.sbin/atm/scspd/scsp_hfsm.c
new file mode 100644
index 0000000..8010290
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_hfsm.c
@@ -0,0 +1,577 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * HELLO finite state machine
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * HELLO FSM actions
+ */
+#define HELLO_ACTION_CNT 7
+int scsp_hello_act_00(Scsp_dcs *, Scsp_msg *);
+int scsp_hello_act_01(Scsp_dcs *, Scsp_msg *);
+int scsp_hello_act_02(Scsp_dcs *, Scsp_msg *);
+int scsp_hello_act_03(Scsp_dcs *, Scsp_msg *);
+int scsp_hello_act_04(Scsp_dcs *, Scsp_msg *);
+int scsp_hello_act_05(Scsp_dcs *, Scsp_msg *);
+int scsp_hello_act_06(Scsp_dcs *, Scsp_msg *);
+
+static int (*scsp_action_vector[HELLO_ACTION_CNT])() = {
+ scsp_hello_act_00,
+ scsp_hello_act_01,
+ scsp_hello_act_02,
+ scsp_hello_act_03,
+ scsp_hello_act_04,
+ scsp_hello_act_05,
+ scsp_hello_act_06
+};
+
+/*
+ * HELLO FSM state table
+ */
+static int hello_state_table[SCSP_HFSM_EVENT_CNT][SCSP_HFSM_STATE_CNT] = {
+ /* 0 1 2 3 */
+ { 1, 1, 1, 1 }, /* 0 */
+ { 0, 2, 2, 2 }, /* 1 */
+ { 0, 3, 3, 3 }, /* 2 */
+ { 0, 0, 4, 4 }, /* 3 */
+ { 0, 5, 5, 6 }, /* 4 */
+};
+
+/*
+ * HELLO finite state machine
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block for the neighbor
+ * event the event which has occurred
+ * msg pointer to received message, if there is one
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hfsm(dcsp, event, msg)
+ Scsp_dcs *dcsp;
+ int event;
+ Scsp_msg *msg;
+{
+ int action, rc, state;
+
+ /*
+ * Select an action from the state table
+ */
+ state = dcsp->sd_hello_state;
+ action = hello_state_table[event][state];
+ if (scsp_trace_mode & SCSP_TRACE_HFSM) {
+ scsp_trace("HFSM: state=%d, event=%d, action=%d\n",
+ state, event, action);
+ }
+ if (action >= HELLO_ACTION_CNT || action <= 0) {
+ scsp_log(LOG_ERR, "Hello FSM--invalid action %d; state=%d, event=%d",
+ action, dcsp->sd_hello_state, event);
+ abort();
+ }
+
+ /*
+ * Perform the selected action
+ */
+ rc = scsp_action_vector[action](dcsp, msg);
+
+ return(rc);
+}
+
+
+/*
+ * HELLO finite state machine action 0
+ * Unexpected action -- log an error message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message (ignored)
+ *
+ * Returns:
+ * EOPNOTSUPP always returns EOPNOTSUPP
+ *
+ */
+int
+scsp_hello_act_00(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ scsp_log(LOG_ERR, "Hello FSM error--unexpected action, state=%d",
+ dcsp->sd_hello_state);
+ return(EOPNOTSUPP);
+}
+
+
+/*
+ * HELLO finite state machine action 1
+ * VCC open -- send HELLO message, start hello timer, go to Waiting
+ * state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message (ignored)
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_01(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc;
+
+ /*
+ * Cancel the VCC open timer if it's running
+ */
+ HARP_CANCEL(&dcsp->sd_open_t);
+
+ /*
+ * Go to Waiting state
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+
+ /*
+ * Send a Hello message
+ */
+ rc = scsp_send_hello(dcsp);
+ if (rc == 0) {
+ /*
+ * Success--start the Hello timer
+ */
+ HARP_TIMER(&dcsp->sd_hello_h_t, SCSP_HELLO_Interval,
+ scsp_hello_timeout);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * HELLO finite state machine action 2
+ * VCC closed -- notify CA FSM, go to Down state, try to re-open VCC
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message (ignored)
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_02(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc;
+
+ /*
+ * Cancel any current timers
+ */
+ HARP_CANCEL(&dcsp->sd_hello_h_t);
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+
+ /*
+ * Log the loss of the VCC
+ */
+ if (dcsp->sd_hello_state > SCSP_HFSM_WAITING) {
+ scsp_log(LOG_ERR, "VC to %s closed",
+ format_atm_addr(&dcsp->sd_addr));
+ }
+
+ /*
+ * Tell the CA FSM that the conection to the DCS is lost
+ */
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
+
+ /*
+ * Go to Down state
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_DOWN;
+
+ /*
+ * If our ID is lower than the DCS's, wait a second before
+ * trying to connect. This should keep both of us from
+ * trying to connect at the same time, resulting in two
+ * VCCs being open.
+ */
+ if (scsp_cmp_id(&dcsp->sd_server->ss_lsid,
+ &dcsp->sd_dcsid) < 0) {
+ /*
+ * Our ID is lower--start the VCC open timer for one
+ * second so we'll try to open the VCC if the DCS
+ * doesn't do it by then
+ */
+ HARP_TIMER(&dcsp->sd_open_t, 1, scsp_open_timeout);
+ } else {
+ /*
+ * Our ID is higher--try to reopen the VCC immediately
+ */
+ if (scsp_dcs_connect(dcsp)) {
+ /*
+ * Conncect failed -- set a timer and try
+ * again later
+ */
+ HARP_TIMER(&dcsp->sd_open_t, SCSP_Open_Interval,
+ scsp_open_timeout);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * HELLO finite state machine action 3
+ * Hello timer expired -- send HELLO message, restart hello timer
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message (ignored)
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_03(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc;
+
+ /*
+ * Send a Hello message
+ */
+ rc = scsp_send_hello(dcsp);
+ if (rc == 0) {
+ /*
+ * Success--restart the Hello timer
+ */
+ HARP_TIMER(&dcsp->sd_hello_h_t, SCSP_HELLO_Interval,
+ scsp_hello_timeout);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * HELLO finite state machine action 4
+ * Receive timer expired -- if we haven't received any Hellos, notify
+ * CA FSM and go to Waiting state; if we've received Hellos, but we
+ * weren't in the receiver ID list, go to Unidirectional state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message (ignored)
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_04(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc = 0;
+
+ /*
+ * Check whether we'ver received any Hellos lately
+ */
+ if (dcsp->sd_hello_rcvd) {
+ /*
+ * We've had Hellos since the receive timer was
+ * started--go to Unidirectional state
+ */
+ dcsp->sd_hello_rcvd = 0;
+ dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
+ } else {
+ /*
+ * We haven't seen any Hellos at all from the DCS in
+ * hello_interval * dead_factor seconds--go to Waiting
+ * state
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ }
+
+ /*
+ * Notify the CA FSM
+ */
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
+
+ return(rc);
+}
+
+
+/*
+ * HELLO finite state machine action 5
+ * Message received -- Ignore all but HELLO messages; if local server
+ * is in receiver list, notify CA FSM and go to Bidirectional state;
+ * otherwise, go to Unidirectional state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_05(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc;
+ Scsp_id *ridp;
+
+ /*
+ * Null message pointer means message decode failed, so
+ * message must have been invalid. Go to Waiting state.
+ */
+ if (msg == (Scsp_msg *)0) {
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ return(0);
+ }
+
+ /*
+ * Ignore the message if it isn't a Hello
+ */
+ if (msg->sc_msg_type != SCSP_HELLO_MSG) {
+ return(0);
+ }
+
+ /*
+ * Save relevant information about DCS, but don't let him give
+ * us zero for timeout values
+ */
+ if (msg->sc_hello->hello_int) {
+ dcsp->sd_hello_int = msg->sc_hello->hello_int;
+ } else {
+ dcsp->sd_hello_int = 1;
+ }
+ if (msg->sc_hello->dead_factor) {
+ dcsp->sd_hello_df = msg->sc_hello->dead_factor;
+ } else {
+ dcsp->sd_hello_df = 1;
+ }
+ dcsp->sd_dcsid = msg->sc_hello->hello_mcp.sid;
+
+ /*
+ * Check the message for the local server's ID
+ */
+ for (ridp = &msg->sc_hello->hello_mcp.rid;
+ ridp;
+ ridp = ridp->next) {
+ if (scsp_cmp_id(&dcsp->sd_server->ss_lsid, ridp) == 0) {
+ /*
+ * Cancel and restart the receive timer
+ */
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ HARP_TIMER(&dcsp->sd_hello_rcv_t,
+ dcsp->sd_hello_int * dcsp->sd_hello_df,
+ scsp_hello_rcv_timeout);
+
+ /*
+ * Go to Bidirectional state and notify the
+ * CA FSM that the connection is up
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_BI_DIR;
+ rc = scsp_cafsm(dcsp,
+ SCSP_CAFSM_HELLO_UP,
+ (void *)0);
+ return(rc);
+ }
+ }
+
+ /*
+ * We weren't in the receiver ID list, so go to
+ * Unidirectional state
+ */
+ dcsp->sd_hello_state = SCSP_HFSM_UNI_DIR;
+
+ return(0);
+}
+
+
+/*
+ * HELLO finite state machine action 6
+ * Message received -- if message is not a HELLO, pass it to the CA
+ * FSM; otherwise, if local server is not in receiver list, notify
+ * CA FSM and go to Unidirectional state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_hello_act_06(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int rc = 0, rcv_found;
+ Scsp_id *ridp;
+
+ /*
+ * Null message pointer means message decode failed, so
+ * message must have been invalid. Go to Waiting state.
+ */
+ if (msg == (Scsp_msg *)0) {
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_HELLO_DOWN, (void *)0);
+ return(rc);
+ }
+
+ /*
+ * Process the message depending on its type
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CA_MSG, (void *)msg);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REQ, (void *)msg);
+ break;
+ case SCSP_CSU_REPLY_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSU_REPLY,
+ (void *)msg);
+ break;
+ case SCSP_CSUS_MSG:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CSUS_MSG, (void *)msg);
+ break;
+ case SCSP_HELLO_MSG:
+ /*
+ * Make sure DCS info is consistent. The sender ID,
+ * family ID, protocol ID, and server group ID are
+ * checked.
+ */
+ if (scsp_cmp_id(&msg->sc_hello->hello_mcp.sid,
+ &dcsp->sd_dcsid) ||
+ (msg->sc_hello->family_id !=
+ dcsp->sd_server->ss_fid) ||
+ (msg->sc_hello->hello_mcp.pid !=
+ dcsp->sd_server->ss_pid) ||
+ (msg->sc_hello->hello_mcp.sgid !=
+ dcsp->sd_server->ss_sgid)) {
+ /*
+ * Bad info--revert to waiting state
+ */
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ dcsp->sd_hello_state = SCSP_HFSM_WAITING;
+ rc = scsp_cafsm(dcsp,
+ SCSP_CAFSM_HELLO_DOWN,
+ (void *)0);
+ return(rc);
+ }
+
+ /*
+ * Mark the arrival of the Hello message
+ */
+ dcsp->sd_hello_rcvd = 1;
+
+ /*
+ * Check the message for the local server's ID
+ */
+ for (ridp = &msg->sc_hello->hello_mcp.rid,
+ rcv_found = 0;
+ ridp;
+ ridp = ridp->next) {
+ rcv_found = (scsp_cmp_id(ridp,
+ &dcsp->sd_server->ss_lsid) == 0);
+ }
+
+ if (rcv_found) {
+ /*
+ * The LS ID was in the list of receiver IDs--
+ * Reset the Hello receive timer
+ */
+ dcsp->sd_hello_rcvd = 0;
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ HARP_TIMER(&dcsp->sd_hello_rcv_t,
+ dcsp->sd_hello_int *
+ dcsp->sd_hello_df,
+ scsp_hello_rcv_timeout);
+ }
+ break;
+ }
+
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_if.c b/usr.sbin/atm/scspd/scsp_if.c
new file mode 100644
index 0000000..9d88f0a
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_if.c
@@ -0,0 +1,626 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Interface to client server protocol
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * SCSP client server interface FSM actions
+ */
+#define SCSP_CIFSM_ACTION_CNT 11
+int scsp_client_act_00(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_01(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_02(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_03(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_04(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_05(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_06(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_07(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_08(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_09(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+int scsp_client_act_10(Scsp_dcs *, Scsp_msg *, Scsp_if_msg *);
+
+static int (*scsp_action_vector[SCSP_CIFSM_ACTION_CNT])() = {
+ scsp_client_act_00,
+ scsp_client_act_01,
+ scsp_client_act_02,
+ scsp_client_act_03,
+ scsp_client_act_04,
+ scsp_client_act_05,
+ scsp_client_act_06,
+ scsp_client_act_07,
+ scsp_client_act_08,
+ scsp_client_act_09,
+ scsp_client_act_10
+};
+
+
+/*
+ * Client server interface FSM state table
+ */
+static int client_state_table[SCSP_CIFSM_EVENT_CNT][SCSP_CIFSM_STATE_CNT] = {
+ /* 0 1 2 3 */
+ { 1, 3, 3, 3 }, /* 0 */
+ { 2, 5, 5, 5 }, /* 1 */
+ { 0, 4, 0, 0 }, /* 2 */
+ { 0, 6, 6, 1 }, /* 3 */
+ { 1, 0, 7, 7 }, /* 4 */
+ { 7, 7, 7, 7 }, /* 5 */
+ { 1, 1, 8, 8 }, /* 6 */
+ { 0, 0, 10, 10 }, /* 7 */
+ { 0, 0, 1, 1 }, /* 8 */
+ { 0, 0, 9, 9 } /* 9 */
+};
+
+
+/*
+ * SCSP client server interface finite state machine
+ *
+ * Arguments:
+ * ssp pointer to server control block
+ * event the event which has occurred
+ * msg pointer to message from DCS, if there is one
+ * cmsg pointer to message from server, if there is one
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_cfsm(dcsp, event, msg, cmsg)
+ Scsp_dcs *dcsp;
+ int event;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int action, rc, state;
+
+ /*
+ * Select an action from the state table
+ */
+ state = dcsp->sd_client_state;
+ action = client_state_table[event][state];
+ if (scsp_trace_mode & SCSP_TRACE_CFSM) {
+ scsp_trace("Server I/F FSM: state=%d, event=%d, action=%d\n",
+ state, event, action);
+ }
+ if (action >= SCSP_CIFSM_ACTION_CNT || action <= 0) {
+ scsp_log(LOG_ERR, "Server I/F FSM--invalid action %d; state=%d, event=%d",
+ action, dcsp->sd_client_state, event);
+ exit(1);
+ }
+
+ /*
+ * Perform the selected action
+ */
+ rc = scsp_action_vector[action](dcsp, msg, cmsg);
+
+ return(rc);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 0
+ * Unexpected action -- log an error message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS (ignored)
+ * cmsg pointer to message from server (ignored)
+ *
+ * Returns:
+ * EOPNOTSUPP always returns EOPNOTSUPP
+ *
+ */
+int
+scsp_client_act_00(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ scsp_log(LOG_ERR, "Server I/F FSM error--unexpected action, state=%d",
+ dcsp->sd_client_state);
+ return(EOPNOTSUPP);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 1
+ *
+ * Ignore an event
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 always returns 0
+ *
+ */
+int
+scsp_client_act_01(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 2
+ *
+ * CA FSM went to Cache Summarize state--go to Summarize
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_02(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ /*
+ * Set the new state
+ */
+ dcsp->sd_client_state = SCSP_CIFSM_SUM;
+
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 3
+ *
+ * CA FSM went down--clean up and go to Null
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_03(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ /*
+ * Set the new state
+ */
+ dcsp->sd_client_state = SCSP_CIFSM_NULL;
+
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 4
+ *
+ * CA FSM went to Update Cache state--go to Update state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_04(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ /*
+ * Set the new state
+ */
+ dcsp->sd_client_state = SCSP_CIFSM_UPD;
+
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 5
+ *
+ * The CA FSM went to Cache Summarize state from Summarize,
+ * Update, or Aligned, implying that the CA FSM went down and came
+ * back up--copy the server's cache to the DCSs CSAS list and go to
+ * Summarize state
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_05(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int i;
+ Scsp_cse *csep, *ncsep;
+
+ /*
+ * Copy the cache summmary to the CSAS list
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (csep = dcsp->sd_server->ss_cache[i]; csep;
+ csep = csep->sc_next) {
+ ncsep = scsp_dup_cse(csep);
+ LINK2TAIL(ncsep, Scsp_cse, dcsp->sd_ca_csas,
+ sc_next);
+ }
+ }
+
+ /*
+ * Set the new state
+ */
+ dcsp->sd_client_state = SCSP_CIFSM_SUM;
+
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 6
+ *
+ * CA FSM went to Aligned state--go to Aligned
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_06(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ /*
+ * Set the new state
+ */
+ dcsp->sd_client_state = SCSP_CIFSM_ALIGN;
+
+ return(0);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 7
+ *
+ * We received a Solicit Rsp or Update Req from the server--pass it
+ * to the CA FSM
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_07(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc;
+ Scsp_csa *csap;
+ Scsp_atmarp_csa *acp;
+
+ /*
+ * Allocate memory for a CSA record
+ */
+ csap = calloc(1, sizeof(Scsp_csa));
+ if (csap == NULL)
+ scsp_mem_err("scsp_client_act_07: sizeof(Scsp_csa)");
+ acp = calloc(1, sizeof(Scsp_atmarp_csa));
+ if (acp == NULL)
+ scsp_mem_err("scsp_client_act_07: sizeof(Scsp_atmarp_csa)");
+
+ /*
+ * Build a CSA record from the server's message
+ */
+ csap->hops = dcsp->sd_hops;
+ csap->null = (cmsg->si_atmarp.sa_state == SCSP_ASTATE_DEL) ||
+ (cmsg->si_type == SCSP_SOLICIT_RSP &&
+ cmsg->si_rc != SCSP_RSP_OK);
+ csap->seq = cmsg->si_atmarp.sa_seq;
+ csap->key = cmsg->si_atmarp.sa_key;
+ csap->oid = cmsg->si_atmarp.sa_oid;
+ csap->atmarp_data = acp;
+ acp->sa_state = cmsg->si_atmarp.sa_state;
+ acp->sa_sha = cmsg->si_atmarp.sa_cha;
+ acp->sa_ssa = cmsg->si_atmarp.sa_csa;
+ acp->sa_spa = cmsg->si_atmarp.sa_cpa;
+ acp->sa_tpa = cmsg->si_atmarp.sa_cpa;
+
+ /*
+ * Call the CA FSM
+ */
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CACHE_UPD, (void *)csap);
+
+ return(rc);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 8
+ *
+ * Update Rsp from server--pass the update to the CA FSM.
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_08(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc;
+
+ /*
+ * Pass the response to the CA FSM
+ */
+ switch (dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ rc = scsp_cafsm(dcsp, SCSP_CAFSM_CACHE_RSP, cmsg);
+ break;
+ default:
+ rc = EPROTONOSUPPORT;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 9
+ *
+ * CSU Solicit from DCS--pass Solicit Ind to server
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_09(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc, rrc = 0;
+ Scsp_csa *csap;
+ Scsp_if_msg *csip;
+
+ /*
+ * Get memory for a Solicit Ind
+ */
+ csip = calloc(1, sizeof(Scsp_if_msg));
+ if (csip == NULL)
+ scsp_mem_err("scsp_client_act_09: sizeof(Scsp_if_msg)");
+
+ /*
+ * Loop through list of CSAs
+ */
+ for (csap = msg->sc_csu_msg->csu_csa_rec; csap;
+ csap = csap->next) {
+ /*
+ * Fill out the Solicit Indication
+ */
+ bzero(csip, sizeof(Scsp_if_msg));
+ csip->si_type = SCSP_SOLICIT_IND;
+ csip->si_proto = dcsp->sd_server->ss_pid;
+ csip->si_tok = (u_long)dcsp;
+ csip->si_len = sizeof(Scsp_if_msg_hdr) +
+ sizeof(Scsp_sum_msg);
+ csip->si_sum.ss_hops = csap->hops;
+ csip->si_sum.ss_null = csap->null;
+ csip->si_sum.ss_seq = csap->seq;
+ csip->si_sum.ss_key = csap->key;
+ csip->si_sum.ss_oid = csap->oid;
+
+ /*
+ * Send the Solicit Ind to the server
+ */
+ rc = scsp_if_sock_write(dcsp->sd_server->ss_sock, csip);
+ if (rc) {
+ rrc = rc;
+ }
+ }
+ free(csip);
+ return(rrc);
+}
+
+
+/*
+ * SCSP client server interface finite state machine action 10
+ *
+ * CSU Request from DCS--pass it to the server as a Cache Update
+ * Indication
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message from DCS
+ * cmsg pointer to message from server
+ *
+ * Returns:
+ * 0 success
+ * else errno describing error
+ *
+ */
+int
+scsp_client_act_10(dcsp, msg, cmsg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ Scsp_if_msg *cmsg;
+{
+ int rc, rrc = 0;
+ Scsp_csa *csap;
+ Scsp_atmarp_csa *acp;
+ Scsp_if_msg *cuip;
+
+ /*
+ * Get memory for a Cache Update Ind
+ */
+ cuip = malloc(sizeof(Scsp_if_msg));
+ if (cuip == NULL)
+ scsp_mem_err("scsp_client_act_10: sizeof(Scsp_if_msg)");
+
+ /*
+ * Loop through CSAs in message
+ */
+ for (csap = msg->sc_csu_msg->csu_csa_rec; csap;
+ csap = csap->next) {
+ acp = csap->atmarp_data;
+ if (!acp)
+ continue;
+
+ /*
+ * Fill out the Cache Update Ind
+ */
+ bzero(cuip, sizeof(Scsp_if_msg));
+ cuip->si_type = SCSP_UPDATE_IND;
+ cuip->si_proto = dcsp->sd_server->ss_pid;
+ cuip->si_tok = (u_long)dcsp;
+ switch(dcsp->sd_server->ss_pid) {
+ case SCSP_PROTO_ATMARP:
+ cuip->si_len = sizeof(Scsp_if_msg_hdr) +
+ sizeof(Scsp_atmarp_msg);
+ cuip->si_atmarp.sa_state = acp->sa_state;
+ cuip->si_atmarp.sa_cpa = acp->sa_spa;
+ cuip->si_atmarp.sa_cha = acp->sa_sha;
+ cuip->si_atmarp.sa_csa = acp->sa_ssa;
+ cuip->si_atmarp.sa_key = csap->key;
+ cuip->si_atmarp.sa_oid = csap->oid;
+ cuip->si_atmarp.sa_seq = csap->seq;
+ break;
+ case SCSP_PROTO_NHRP:
+ /*
+ * Not implemented yet
+ */
+ break;
+ }
+
+ /*
+ * Send the Cache Update Ind to the server
+ */
+ rc = scsp_if_sock_write(dcsp->sd_server->ss_sock, cuip);
+ if (rc) {
+ rrc = rc;
+ }
+ }
+ free(cuip);
+ return(rrc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_if.h b/usr.sbin/atm/scspd/scsp_if.h
new file mode 100644
index 0000000..844ee2c
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_if.h
@@ -0,0 +1,194 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Interface to server clients of SCSP
+ *
+ */
+
+#ifndef _SCSP_SCSP_IF_H
+#define _SCSP_SCSP_IF_H
+
+
+/*
+ * SCSP configuration message
+ */
+struct scsp_cfg_msg {
+ char atmarp_netif[IFNAMSIZ];
+};
+typedef struct scsp_cfg_msg Scsp_cfg_msg;
+
+
+/*
+ * SCSP cache summary
+ */
+struct scsp_sum_msg {
+ u_short ss_hops; /* Hop count */
+ u_char ss_null; /* Null flag */
+ long ss_seq; /* CSA seq. no. */
+ Scsp_ckey ss_key; /* Cache key */
+ Scsp_id ss_oid; /* Originator ID */
+};
+typedef struct scsp_sum_msg Scsp_sum_msg;
+
+
+/*
+ * SCSP constants for ATMARP
+ */
+#define SCSP_ATMARP_PROTO 1
+#define SCSP_ATMARP_SIDL 4
+#define SCSP_ATMARP_RIDL 4
+#define SCSP_ATMARP_CKL 4
+#define SCSP_ATMARP_OIDL 4
+
+
+/*
+ * SCSP ATMARP message
+ */
+struct scsp_atmarp_msg {
+ u_char sa_state; /* Cache entry state (below) */
+ struct in_addr sa_cpa; /* Cached protocol address */
+ Atm_addr sa_cha; /* Cached ATM address */
+ Atm_addr sa_csa; /* Cached ATM subaddress */
+ Scsp_ckey sa_key; /* Cache key for entry */
+ Scsp_id sa_oid; /* Originator ID */
+ long sa_seq; /* Sequence no. */
+};
+typedef struct scsp_atmarp_msg Scsp_atmarp_msg;
+
+#define SCSP_ASTATE_NEW 0 /* ATMARP new server registration */
+#define SCSP_ASTATE_UPD 1 /* ATMARP server refreshed */
+#define SCSP_ASTATE_DEL 2 /* ATMARP server data deleted */
+
+
+/*
+ * SCSP constants for NHRP
+ */
+#define SCSP_NHRP_PROTO 2
+#define SCSP_NHRP_SIDL 4
+#define SCSP_NHRP_RIDL 4
+#define SCSP_NHRP_CKL 4
+#define SCSP_NHRP_OIDL 4
+
+
+/*
+ * SCSP NHRP message
+ */
+struct scsp_nhrp_msg {
+ u_short sn_af; /* Address family */
+ u_short sn_proto; /* NHRP protocol type */
+ u_char sn_snap[5]; /* SNAP */
+ u_char sn_ver; /* NHRP version number */
+ u_short sn_flags; /* Flags */
+ u_long sn_rid; /* Request ID */
+ u_char sn_state; /* State */
+ u_char sn_prel; /* Prefix length */
+ u_short sn_mtu; /* Maximum transmission unit */
+ u_short sn_hold; /* Holding time */
+ Atm_addr sn_addr; /* Server network address */
+ Atm_addr sn_saddr; /* Server network subaddress */
+ struct in_addr sn_paddr; /* Server protocol address */
+ Scsp_ckey sn_key; /* Cache key for entry */
+ Scsp_id sn_oid; /* Originator ID */
+};
+typedef struct scsp_nhrp_msg Scsp_nhrp_msg;
+
+#define SCSP_NSTATE_NEW 0 /* New NHRP server */
+#define SCSP_NSTATE_UPD 1 /* NHRP server re-registered */
+#define SCSP_NSTATE_DEL 2 /* NHRP server data purged */
+#define SCSP_NSTATE_NSD 3 /* NHRP no such data in server */
+
+
+/*
+ * SCSP/server message header
+ */
+struct scsp_if_msg_hdr {
+ u_char sh_type; /* Message type */
+ u_char sh_rc; /* Response code */
+ u_short sh_proto; /* SCSP protocol ID */
+ int sh_len; /* Length of message */
+ u_long sh_tok; /* Token from SCSP daemon */
+};
+typedef struct scsp_if_msg_hdr Scsp_if_msg_hdr;
+
+
+/*
+ * SCSP-server message
+ */
+struct scsp_if_msg {
+ Scsp_if_msg_hdr si_hdr; /* Header fields */
+ union {
+ Scsp_cfg_msg siu_cfg; /* Config data */
+ Scsp_sum_msg siu_sum; /* Cache summary */
+ Scsp_atmarp_msg siu_atmarp; /* ATMARP update */
+ Scsp_nhrp_msg siu_nhrp; /* NHRP update */
+ } si_u;
+};
+typedef struct scsp_if_msg Scsp_if_msg;
+
+#define si_type si_hdr.sh_type
+#define si_rc si_hdr.sh_rc
+#define si_proto si_hdr.sh_proto
+#define si_len si_hdr.sh_len
+#define si_tok si_hdr.sh_tok
+
+#define si_cfg si_u.siu_cfg
+#define si_sum si_u.siu_sum
+#define si_atmarp si_u.siu_atmarp
+#define si_nhrp si_u.siu_nhrp
+
+
+/*
+ * Message types
+ */
+#define SCSP_NOP_REQ 1
+#define SCSP_CFG_REQ 2
+#define SCSP_CFG_RSP 3
+#define SCSP_CACHE_IND 4
+#define SCSP_CACHE_RSP 5
+#define SCSP_SOLICIT_IND 6
+#define SCSP_SOLICIT_RSP 7
+#define SCSP_UPDATE_IND 8
+#define SCSP_UPDATE_REQ 9
+#define SCSP_UPDATE_RSP 10
+
+
+/*
+ * Response codes
+ */
+#define SCSP_RSP_OK 0
+#define SCSP_RSP_ERR 1
+#define SCSP_RSP_REJ 2
+#define SCSP_RSP_NOT_FOUND 3
+
+
+#endif /* _SCSP_SCSP_IF_H */
diff --git a/usr.sbin/atm/scspd/scsp_input.c b/usr.sbin/atm/scspd/scsp_input.c
new file mode 100644
index 0000000..2e3fbb0
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_input.c
@@ -0,0 +1,1088 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Input packet processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+static int scsp_parse_atmarp(char *, int, Scsp_atmarp_csa **);
+
+
+/*
+ * Get a long ingeter
+ *
+ * This routine is provided to handle long integers that may not
+ * be word-aligned in the input buffer.
+ *
+ * Arguments:
+ * cp pointer to long int in message
+ *
+ * Returns:
+ * int long int in host order
+ *
+ */
+static u_long
+get_long(cp)
+ u_char *cp;
+{
+ int i;
+ u_long l;
+
+ /*
+ * Read the long out of the input buffer
+ */
+ l = 0;
+ for (i = 0; i < sizeof(u_long); i++)
+ l = (l << 8) + *cp++;
+
+ /*
+ * Return the value in host order
+ */
+ return(l);
+}
+
+
+/*
+ * Free an SCSP Cache Alignment message in internal format
+ *
+ * Arguments:
+ * cap pointer to CA message
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+scsp_free_ca(cap)
+ Scsp_ca *cap;
+{
+ Scsp_csa *csap, *ncsap;
+
+ /*
+ * Return if there's nothing to free
+ */
+ if (cap == (Scsp_ca *)0)
+ return;
+
+ /*
+ * Free the CSAS records
+ */
+ for (csap = cap->ca_csa_rec; csap; csap = ncsap) {
+ ncsap = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+ /*
+ * Free the CA message structure
+ */
+ free(cap);
+}
+
+
+/*
+ * Free an SCSP Cache State Update Request, Cache State Update Reply,
+ * or Cache State Update Solicit message in internal format
+ *
+ * Arguments:
+ * csup pointer to CSU message
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+scsp_free_csu(csup)
+ Scsp_csu_msg *csup;
+{
+ Scsp_csa *csap, *ncsap;
+
+ /*
+ * Return if there's nothing to free
+ */
+ if (csup == (Scsp_csu_msg *)0)
+ return;
+
+ /*
+ * Free the CSA records
+ */
+ for (csap = csup->csu_csa_rec; csap; csap = ncsap) {
+ ncsap = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free the CSU message structure
+ */
+ free(csup);
+}
+
+
+/*
+ * Free an SCSP Hello message in internal format
+ *
+ * Arguments:
+ * hp pointer to Hello message
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+scsp_free_hello(hp)
+ Scsp_hello *hp;
+{
+ /*
+ * Return if there's nothing to free
+ */
+ if (hp == (Scsp_hello *)0)
+ return;
+
+ /*
+ * Free the Hello message structure
+ */
+ free(hp);
+}
+
+
+/*
+ * Free an SCSP message in internal format
+ *
+ * Arguments:
+ * msg pointer to input packet
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_free_msg(msg)
+ Scsp_msg *msg;
+{
+ Scsp_ext *exp, *nexp;
+
+ /*
+ * Return if there's nothing to free
+ */
+ if (msg == (Scsp_msg *)0)
+ return;
+
+ /*
+ * Free the message body
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ scsp_free_ca(msg->sc_ca);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ scsp_free_csu(msg->sc_csu_msg);
+ break;
+ case SCSP_HELLO_MSG:
+ scsp_free_hello(msg->sc_hello);
+ break;
+ }
+
+ /*
+ * Free any extensions
+ */
+ for (exp = msg->sc_ext; exp; exp = nexp) {
+ nexp = exp->next;
+ free(exp);
+ }
+
+ /*
+ * Free the message structure
+ */
+ free(msg);
+}
+
+
+/*
+ * Parse a Sender or Receiver ID
+ *
+ * Arguments:
+ * buff pointer to ID
+ * id_len length of ID
+ * idp pointer to structure to receive the ID
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of ID processed
+ *
+ */
+static int
+scsp_parse_id(buff, id_len, idp)
+ char *buff;
+ int id_len;
+ Scsp_id *idp;
+{
+ /*
+ * Sanity check
+ */
+ if (!buff ||
+ id_len == 0 || id_len > SCSP_MAX_ID_LEN ||
+ !idp) {
+ return(0);
+ }
+
+ /*
+ * Save the ID length
+ */
+ idp->id_len = id_len;
+
+ /*
+ * Get the ID
+ */
+ bcopy(buff, idp->id, id_len);
+
+ /*
+ * Return the ID length
+ */
+ return(id_len);
+}
+
+
+/*
+ * Parse the Mandatory Common Part of an SCSP input packet
+ *
+ * Arguments:
+ * buff pointer to mandatory common part
+ * pdu_len length of input packet
+ * mcp pointer to location of MCP in decoded record
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of MCP in message
+ *
+ */
+static int
+scsp_parse_mcp(buff, pdu_len, mcp)
+ char *buff;
+ int pdu_len;
+ Scsp_mcp *mcp;
+{
+ int len;
+ u_char *idp;
+ struct scsp_nmcp *smp;
+
+ /*
+ * Get the protocol ID
+ */
+ smp = (struct scsp_nmcp *)buff;
+ mcp->pid = ntohs(smp->sm_pid);
+ if (mcp->pid < SCSP_PROTO_ATMARP ||
+ mcp->pid > SCSP_PROTO_LNNI) {
+ /* Protocol ID is invalid */
+ goto mcp_invalid;
+ }
+
+ /*
+ * Get the server group ID
+ */
+ mcp->sgid = ntohs(smp->sm_sgid);
+
+ /*
+ * Get the flags
+ */
+ mcp->flags = ntohs(smp->sm_flags);
+
+ /*
+ * Get the sender ID and length
+ */
+ idp = (u_char *) ((caddr_t)smp + sizeof(struct scsp_nmcp));
+ len = scsp_parse_id(idp, smp->sm_sid_len, &mcp->sid);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Get the receiver ID and length
+ */
+ idp += len;
+ len = scsp_parse_id(idp, smp->sm_rid_len, &mcp->rid);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Get the record count
+ */
+ mcp->rec_cnt = ntohs(smp->sm_rec_cnt);
+
+ /*
+ * Return the length of data we processed
+ */
+ return(sizeof(struct scsp_nmcp) + smp->sm_sid_len +
+ smp->sm_rid_len);
+
+mcp_invalid:
+ return(0);
+}
+
+
+/*
+ * Parse an Extension
+ *
+ * Arguments:
+ * buff pointer to Extension
+ * pdu_len length of buffer
+ * expp pointer to location to receive pointer to the Extension
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of Extension processed
+ *
+ */
+static int
+scsp_parse_ext(buff, pdu_len, expp)
+ char *buff;
+ int pdu_len;
+ Scsp_ext **expp;
+{
+ int len;
+ struct scsp_next *sep;
+ Scsp_ext *exp;
+
+ /*
+ * Get memory for the extension
+ */
+ sep = (struct scsp_next *)buff;
+ len = sizeof(Scsp_ext) + ntohs(sep->se_len);
+ exp = calloc(1, len);
+ if (exp == NULL)
+ goto ext_invalid;
+
+ /*
+ * Get the type
+ */
+ exp->type = ntohs(sep->se_type);
+
+ /*
+ * Get the length
+ */
+ exp->len = ntohs(sep->se_len);
+
+ /*
+ * Get the value
+ */
+ if (exp->len > 0) {
+ bcopy((caddr_t)sep + sizeof(struct scsp_next),
+ (caddr_t)exp + sizeof(Scsp_ext),
+ exp->len);
+ }
+
+ /*
+ * Save a pointer to the extension and return the
+ * number of bytes processed
+ */
+ *expp = exp;
+ return(sizeof(struct scsp_next) + exp->len);
+
+ext_invalid:
+ if (exp) {
+ free(exp);
+ }
+ return(0);
+}
+
+
+/*
+ * Parse a Cache State Advertisement or Cache State Advertisement
+ * Summary record
+ *
+ * Arguments:
+ * buff pointer to CSA or CSAS record
+ * pdu_len length of input packet
+ * csapp pointer to location to put pointer to CSA or CSAS
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of record processed
+ *
+ */
+static int
+scsp_parse_csa(buff, pdu_len, csapp)
+ char *buff;
+ int pdu_len;
+ Scsp_csa **csapp;
+{
+ int len;
+ char *idp;
+ struct scsp_ncsa *scp;
+ Scsp_csa *csap = NULL;
+
+ /*
+ * Check the record length
+ */
+ scp = (struct scsp_ncsa *)buff;
+ if (ntohs(scp->scs_len) < (sizeof(struct scsp_ncsa) +
+ scp->scs_ck_len + scp->scs_oid_len)) {
+ goto csa_invalid;
+ }
+
+ /*
+ * Get memory for the returned structure
+ */
+ len = sizeof(Scsp_csa) + ntohs(scp->scs_len) -
+ sizeof(struct scsp_ncsa) - scp->scs_ck_len -
+ scp->scs_oid_len;
+ csap = calloc(1, len);
+ if (csap == NULL)
+ goto csa_invalid;
+
+ /*
+ * Get the hop count
+ */
+ csap->hops = ntohs(scp->scs_hop_cnt);
+
+ /*
+ * Set the null flag
+ */
+ csap->null = (ntohs(scp->scs_nfill) & SCSP_CSAS_NULL) != 0;
+
+ /*
+ * Get the sequence number
+ */
+ csap->seq = get_long((u_char *)&scp->scs_seq);
+
+ /*
+ * Get the cache key
+ */
+ if (scp->scs_ck_len == 0 ||
+ scp->scs_ck_len > SCSP_MAX_KEY_LEN) {
+ goto csa_invalid;
+ }
+ csap->key.key_len = scp->scs_ck_len;
+ idp = (char *) ((caddr_t)scp + sizeof(struct scsp_ncsa));
+ bcopy(idp, csap->key.key, scp->scs_ck_len);
+
+ /*
+ * Get the originator ID
+ */
+ idp += scp->scs_ck_len;
+ len = scsp_parse_id(idp, scp->scs_oid_len, &csap->oid);
+ if (len == 0) {
+ goto csa_invalid;
+ }
+
+ /*
+ * Get the protocol-specific data, if present
+ */
+ len = ntohs(scp->scs_len) - (sizeof(struct scsp_ncsa) +
+ scp->scs_ck_len + scp->scs_oid_len);
+ if (len > 0) {
+ idp += scp->scs_oid_len;
+ len = scsp_parse_atmarp(idp, len, &csap->atmarp_data);
+ if (len == 0)
+ goto csa_invalid;
+ }
+
+ /*
+ * Set a pointer to the MCP and return the length
+ * of data we processed
+ */
+ *csapp = csap;
+ return(ntohs(scp->scs_len));
+
+csa_invalid:
+ if (csap)
+ SCSP_FREE_CSA(csap);
+ return(0);
+}
+
+
+/*
+ * Parse a Cache Alignment message
+ *
+ * Arguments:
+ * buff pointer to start of CA in message
+ * pdu_len length of input packet
+ * capp pointer to location to put pointer to CA message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CA message processed
+ *
+ */
+static int
+scsp_parse_ca(buff, pdu_len, capp)
+ char *buff;
+ int pdu_len;
+ Scsp_ca **capp;
+{
+ int i, len, proc_len;
+ struct scsp_nca *scap;
+ Scsp_ca *cap;
+ Scsp_csa **csapp;
+
+ /*
+ * Get memory for the returned structure
+ */
+ scap = (struct scsp_nca *)buff;
+ cap = calloc(1, sizeof(Scsp_ca));
+ if (cap == NULL)
+ goto ca_invalid;
+
+ /*
+ * Get the sequence number
+ */
+ cap->ca_seq = get_long((u_char *)&scap->sca_seq);
+ proc_len = sizeof(scap->sca_seq);
+ buff += sizeof(scap->sca_seq);
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ len = scsp_parse_mcp(buff,
+ pdu_len - proc_len,
+ &cap->ca_mcp);
+ if (len == 0)
+ goto ca_invalid;
+ buff += len;
+ proc_len += len;
+
+ /*
+ * Set the flags
+ */
+ cap->ca_m = (cap->ca_mcp.flags & SCSP_CA_M) != 0;
+ cap->ca_i = (cap->ca_mcp.flags & SCSP_CA_I) != 0;
+ cap->ca_o = (cap->ca_mcp.flags & SCSP_CA_O) != 0;
+
+ /*
+ * Get the CSAS records from the message
+ */
+ for (i = 0, csapp = &cap->ca_csa_rec; i < cap->ca_mcp.rec_cnt;
+ i++, csapp = &(*csapp)->next) {
+ len = scsp_parse_csa(buff, pdu_len - proc_len, csapp);
+ buff += len;
+ proc_len += len;
+ }
+
+ /*
+ * Set the address of the CA message and
+ * return the length of processed data
+ */
+ *capp = cap;
+ return(proc_len);
+
+ca_invalid:
+ if (cap)
+ scsp_free_ca(cap);
+ return(0);
+}
+
+
+/*
+ * Parse the ATMARP-specific part of a CSA record
+ *
+ * Arguments:
+ * buff pointer to ATMARP part of CSU message
+ * pdu_len length of data to process
+ * acspp pointer to location to put pointer to CSU message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CSU Req message processed
+ *
+ */
+static int
+scsp_parse_atmarp(buff, pdu_len, acspp)
+ char *buff;
+ int pdu_len;
+ Scsp_atmarp_csa **acspp;
+{
+ int len, proc_len;
+ struct scsp_atmarp_ncsa *sacp;
+ Scsp_atmarp_csa *acsp = NULL;
+
+ /*
+ * Initial packet verification
+ */
+ sacp = (struct scsp_atmarp_ncsa *)buff;
+ if ((sacp->sa_hrd != ntohs(ARP_ATMFORUM)) ||
+ (sacp->sa_pro != ntohs(ETHERTYPE_IP)))
+ goto acs_invalid;
+
+ /*
+ * Get memory for the returned structure
+ */
+ acsp = calloc(1, sizeof(Scsp_atmarp_csa));
+ if (acsp == NULL)
+ goto acs_invalid;
+
+ /*
+ * Get state code
+ */
+ acsp->sa_state = sacp->sa_state;
+ proc_len = sizeof(struct scsp_atmarp_ncsa);
+
+ /*
+ * Verify/gather source ATM address
+ */
+ acsp->sa_sha.address_format = T_ATM_ABSENT;
+ acsp->sa_sha.address_length = 0;
+ if ((len = (sacp->sa_shtl & ARP_TL_LMASK)) != 0) {
+ if (sacp->sa_shtl & ARP_TL_E164) {
+ if (len > sizeof(Atm_addr_e164))
+ goto acs_invalid;
+ acsp->sa_sha.address_format = T_ATM_E164_ADDR;
+ } else {
+ if (len != sizeof(Atm_addr_nsap))
+ goto acs_invalid;
+ acsp->sa_sha.address_format = T_ATM_ENDSYS_ADDR;
+ }
+ acsp->sa_sha.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_sha.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather source ATM subaddress
+ */
+ acsp->sa_ssa.address_format = T_ATM_ABSENT;
+ acsp->sa_ssa.address_length = 0;
+ if ((len = (sacp->sa_sstl & ARP_TL_LMASK)) != 0) {
+ if (((sacp->sa_sstl & ARP_TL_TMASK) != ARP_TL_NSAPA) ||
+ (len != sizeof(Atm_addr_nsap)))
+ goto acs_invalid;
+ acsp->sa_ssa.address_format = T_ATM_ENDSYS_ADDR;
+ acsp->sa_ssa.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_ssa.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather source IP address
+ */
+ if ((len = sacp->sa_spln) != 0) {
+ if (len != sizeof(struct in_addr))
+ goto acs_invalid;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)&acsp->sa_spa, len);
+ proc_len += len;
+ } else {
+ acsp->sa_spa.s_addr = 0;
+ }
+
+ /*
+ * Verify/gather target ATM address
+ */
+ acsp->sa_tha.address_format = T_ATM_ABSENT;
+ acsp->sa_tha.address_length = 0;
+ if ((len = (sacp->sa_thtl & ARP_TL_LMASK)) != 0) {
+ if (sacp->sa_thtl & ARP_TL_E164) {
+ if (len > sizeof(Atm_addr_e164))
+ goto acs_invalid;
+ acsp->sa_tha.address_format = T_ATM_E164_ADDR;
+ } else {
+ if (len != sizeof(Atm_addr_nsap))
+ goto acs_invalid;
+ acsp->sa_tha.address_format = T_ATM_ENDSYS_ADDR;
+ }
+ acsp->sa_tha.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_tha.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather target ATM subaddress
+ */
+ acsp->sa_tsa.address_format = T_ATM_ABSENT;
+ acsp->sa_tsa.address_length = 0;
+ if ((len = (sacp->sa_tstl & ARP_TL_LMASK)) != 0) {
+ if (((sacp->sa_tstl & ARP_TL_TMASK) != ARP_TL_NSAPA) ||
+ (len != sizeof(Atm_addr_nsap)))
+ goto acs_invalid;
+ acsp->sa_tsa.address_format = T_ATM_ENDSYS_ADDR;
+ acsp->sa_tsa.address_length = len;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)acsp->sa_tsa.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather target IP address
+ */
+ if ((len = sacp->sa_tpln) != 0) {
+ if (len != sizeof(struct in_addr))
+ goto acs_invalid;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ bcopy(&buff[proc_len], (char *)&acsp->sa_tpa, len);
+ proc_len += len;
+ } else {
+ acsp->sa_tpa.s_addr = 0;
+ }
+
+ /*
+ * Verify packet length
+ */
+ if (proc_len != pdu_len)
+ goto acs_invalid;
+
+ *acspp = acsp;
+ return(proc_len);
+
+acs_invalid:
+ if (acsp)
+ free(acsp);
+ return(0);
+}
+
+
+/*
+ * Parse a Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message. These all have the same format,
+ * a Mandatory Common Part followed by a number of CSA or CSAS records.
+ *
+ * Arguments:
+ * buff pointer to start of CSU message
+ * pdu_len length of input packet
+ * csupp pointer to location to put pointer to CSU message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CSU Req message processed
+ *
+ */
+static int
+scsp_parse_csu(buff, pdu_len, csupp)
+ char *buff;
+ int pdu_len;
+ Scsp_csu_msg **csupp;
+{
+ int i, len, proc_len;
+ Scsp_csu_msg *csup;
+ Scsp_csa **csapp;
+
+ /*
+ * Get memory for the returned structure
+ */
+ csup = calloc(1, sizeof(Scsp_csu_msg));
+ if (csup == NULL)
+ goto csu_invalid;
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ len = scsp_parse_mcp(buff, pdu_len, &csup->csu_mcp);
+ if (len == 0)
+ goto csu_invalid;
+ buff += len;
+ proc_len = len;
+
+ /*
+ * Get the CSAS records from the message
+ */
+ for (i = 0, csapp = &csup->csu_csa_rec;
+ i < csup->csu_mcp.rec_cnt;
+ i++, csapp = &(*csapp)->next) {
+ len = scsp_parse_csa(buff, pdu_len - proc_len, csapp);
+ buff += len;
+ proc_len += len;
+ }
+
+ /*
+ * Set the address of the CSU Req message and
+ * return the length of processed data
+ */
+ *csupp = csup;
+ return(proc_len);
+
+csu_invalid:
+ if (csup)
+ scsp_free_csu(csup);
+ return(0);
+}
+
+
+/*
+ * Parse a Hello message
+ *
+ * Arguments:
+ * buff pointer to start of Hello in message
+ * pdu_len length of input packet
+ * hpp pointer to location to put pointer to Hello message
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of Hello message processed
+ *
+ */
+static int
+scsp_parse_hello(buff, pdu_len, hpp)
+ char *buff;
+ int pdu_len;
+ Scsp_hello **hpp;
+{
+ int i, len, proc_len;
+ struct scsp_nhello *shp = (struct scsp_nhello *)buff;
+ Scsp_hello *hp;
+ Scsp_id *idp;
+ Scsp_id **ridpp;
+
+ /*
+ * Get memory for the returned structure
+ */
+ hp = calloc(1, sizeof(Scsp_hello));
+ if (hp == NULL)
+ goto hello_invalid;
+
+ /*
+ * Get the hello interval
+ */
+ hp->hello_int = ntohs(shp->sch_hi);
+
+ /*
+ * Get the dead factor
+ */
+ hp->dead_factor = ntohs(shp->sch_df);
+
+ /*
+ * Get the family ID
+ */
+ hp->family_id = ntohs(shp->sch_fid);
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ proc_len = sizeof(struct scsp_nhello) -
+ sizeof(struct scsp_nmcp);
+ buff += proc_len;
+ len = scsp_parse_mcp(buff, pdu_len - proc_len,
+ &hp->hello_mcp);
+ if (len == 0)
+ goto hello_invalid;
+ buff += len;
+ proc_len += len;
+
+ /*
+ * Get additional receiver ID records from the message
+ */
+ for (i = 0, ridpp = &hp->hello_mcp.rid.next;
+ i < hp->hello_mcp.rec_cnt;
+ i++, ridpp = &idp->next) {
+ idp = calloc(1, sizeof(Scsp_id));
+ if (idp == NULL)
+ goto hello_invalid;
+ len = scsp_parse_id(buff,
+ hp->hello_mcp.rid.id_len,
+ idp);
+ if (len == 0) {
+ free(idp);
+ goto hello_invalid;
+ }
+ buff += len;
+ proc_len += len;
+ *ridpp = idp;
+ }
+
+ /*
+ * Set the address of the CA message and
+ * return the length of processed data
+ */
+ *hpp = hp;
+ return(proc_len);
+
+hello_invalid:
+ if (hp)
+ scsp_free_hello(hp);
+ return(0);
+}
+
+
+/*
+ * Parse an SCSP input packet
+ *
+ * Arguments:
+ * buff pointer to input packet
+ * pdu_len length of input packet
+ *
+ * Returns:
+ * NULL input packet was invalid
+ * else pointer to packet in internal format
+ *
+ */
+Scsp_msg *
+scsp_parse_msg(buff, pdu_len)
+ char *buff;
+ int pdu_len;
+{
+ int ext_off, len, plen;
+ struct scsp_nhdr *shp;
+ Scsp_msg *msg = (Scsp_msg *)0;
+ Scsp_ext **expp;
+
+ /*
+ * Check the message checksum
+ */
+ if (ip_checksum(buff, pdu_len) != 0) {
+ /*
+ * Checksum was bad--discard the message
+ */
+ goto ignore;
+ }
+
+ /*
+ * Allocate storage for the message
+ */
+ msg = calloc(1, sizeof(Scsp_msg));
+ if (msg == NULL)
+ goto ignore;
+
+ /*
+ * Decode the fixed header
+ *
+ * Check the version
+ */
+ shp = (struct scsp_nhdr *)buff;
+ if (shp->sh_ver != SCSP_VER_1)
+ goto ignore;
+
+ /*
+ * Get the message type
+ */
+ msg->sc_msg_type = shp->sh_type;
+
+ /*
+ * Get and check the length
+ */
+ len = ntohs(shp->sh_len);
+ if (len != pdu_len)
+ goto ignore;
+
+ /*
+ * Get the extension offset
+ */
+ ext_off = ntohs(shp->sh_ext_off);
+
+ /*
+ * Decode the body of the message, depending on the type
+ */
+ buff += sizeof(struct scsp_nhdr);
+ len -= sizeof(struct scsp_nhdr);
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ plen = scsp_parse_ca(buff, len, &msg->sc_ca);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ plen = scsp_parse_csu(buff, len, &msg->sc_csu_msg);
+ break;
+ case SCSP_HELLO_MSG:
+ plen = scsp_parse_hello(buff, len, &msg->sc_hello);
+ break;
+ default:
+ goto ignore;
+ }
+ if (plen == 0) {
+ goto ignore;
+ }
+ buff += plen;
+ len -= plen;
+
+ /*
+ * Decode any extensions
+ */
+ if (ext_off != 0) {
+ for (expp = &msg->sc_ext; len > 0;
+ expp = &(*expp)->next) {
+ plen = scsp_parse_ext(buff, len, expp);
+ if (plen == 0) {
+ goto ignore;
+ }
+ buff += plen;
+ len -= plen;
+ }
+ }
+
+ /*
+ * Make sure we handled the whole message
+ */
+ if (len != 0) {
+ goto ignore;
+ }
+
+ /*
+ * Return the address of the SCSP message in internal format
+ */
+ return(msg);
+
+ignore:
+ if (msg)
+ scsp_free_msg(msg);
+ return(Scsp_msg *)0;
+}
diff --git a/usr.sbin/atm/scspd/scsp_log.c b/usr.sbin/atm/scspd/scsp_log.c
new file mode 100644
index 0000000..8b76c3e
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_log.c
@@ -0,0 +1,264 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP logging routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+FILE *scsp_trace_file = (FILE *)0;
+
+
+/*
+ * Write a message to SCSP's log
+ *
+ * Arguments:
+ * level pointer to an SCSP cache key structure
+ * fmt printf-style format string
+ * ... parameters for printf-style use according to fmt
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+#if __STDC__
+scsp_log(const int level, const char *fmt, ...)
+#else
+scsp_log(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ /*
+ * In debug mode, just write to stdout
+ */
+ if (scsp_debug_mode) {
+ vprintf(fmt, ap);
+ printf("\n");
+ return;
+ }
+
+ /*
+ * Write to syslog if it's active or if no log file is set up
+ */
+ if (scsp_log_syslog || !scsp_log_file) {
+ vsyslog(level, fmt, ap);
+ }
+
+ /*
+ * Write to the log file if there's one set up
+ */
+ if (scsp_log_file) {
+ vfprintf(scsp_log_file, fmt, ap);
+ fprintf(scsp_log_file, "\n");
+ }
+
+ va_end(ap);
+}
+
+
+/*
+ * Open SCSP's trace file
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_open_trace()
+{
+ char fname[64];
+
+ /*
+ * Build a file name
+ */
+ bzero(fname, sizeof(fname));
+ sprintf(fname, "/tmp/scspd.%d.trace", getpid());
+
+ /*
+ * Open the trace file. If the open fails, log an error, but
+ * keep going. The trace routine will notice that the file
+ * isn't open and won't try to write to it.
+ */
+ scsp_trace_file = fopen(fname, "w");
+ if (scsp_trace_file == (FILE *)0) {
+ scsp_log(LOG_ERR, "Can't open trace file");
+ }
+}
+
+
+/*
+ * Write a message to SCSP's trace file
+ *
+ * Arguments:
+ * fmt printf-style format string
+ * ... parameters for printf-style use according to fmt
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+#if __STDC__
+scsp_trace(const char *fmt, ...)
+#else
+scsp_trace(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ /*
+ * Write the message to the trace file, if it's open
+ */
+ if (scsp_trace_file) {
+ vfprintf(scsp_trace_file, fmt, ap);
+ }
+
+ va_end(ap);
+}
+
+
+/*
+ * Write an SCSP message to SCSP's trace file
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for the message
+ * msg pointer to the message
+ * dir a direction indicator--0 for sending, 1 for receiving
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_trace_msg(dcsp, msg, dir)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ int dir;
+{
+ struct in_addr addr;
+
+ /*
+ * Copy the remote IP address into a struct in_addr
+ */
+ bcopy(dcsp->sd_dcsid.id, &addr.s_addr, sizeof(struct in_addr));
+
+ /*
+ * Write the message to the trace file, if it's open
+ */
+ if (scsp_trace_file) {
+ scsp_trace("SCSP message at 0x%x %s %s\n",
+ (u_long)msg,
+ (dir ? "received from" : "sent to"),
+ format_ip_addr(&addr));
+ print_scsp_msg(scsp_trace_file, msg);
+ }
+}
+
+
+/*
+ * Log a memory error and exit
+ *
+ * Arguments:
+ * cp message to log
+ *
+ * Returns:
+ * exits, does not return
+ *
+ */
+void
+scsp_mem_err(cp)
+ char *cp;
+{
+ scsp_log(LOG_CRIT, "out of memory: %s", cp);
+ exit(2);
+}
diff --git a/usr.sbin/atm/scspd/scsp_msg.c b/usr.sbin/atm/scspd/scsp_msg.c
new file mode 100644
index 0000000..63e0e40
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_msg.c
@@ -0,0 +1,590 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP message-handling routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Copy CSAS records into a CA record
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ * cap pointer to CA record for CSASs
+ *
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+scsp_ca_csas_setup(dcsp, cap)
+ Scsp_dcs *dcsp;
+ Scsp_ca *cap;
+{
+ int csas_len, len, mtu;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_cse *csep, *next_csep;
+ Scsp_csa *csap;
+
+ /*
+ * Loop through pending CSAS records
+ */
+ len = sizeof(struct scsp_nhdr) + sizeof(struct scsp_nmcp) +
+ ssp->ss_lsid.id_len +
+ dcsp->sd_dcsid.id_len;
+ csas_len = sizeof(struct scsp_ncsa) +
+ dcsp->sd_server->ss_id_len +
+ dcsp->sd_server->ss_ckey_len;
+ mtu = dcsp->sd_server->ss_mtu;
+ for (csep = dcsp->sd_ca_csas;
+ csep && (len < mtu - csas_len);
+ csep = next_csep) {
+ next_csep = csep->sc_next;
+ csap = scsp_cse2csas(csep);
+ LINK2TAIL(csap, Scsp_csa, cap->ca_csa_rec, next);
+ len += csas_len;
+ UNLINK(csep, Scsp_cse, dcsp->sd_ca_csas, sc_next);
+ free(csep);
+ cap->ca_mcp.rec_cnt++;
+ }
+}
+
+
+/*
+ * Process CSA records from a CSU Request that may be in response to
+ * CSAS records sent in a CSUS
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to received message
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_csus_ack(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ Scsp_csu_msg *csusp;
+ Scsp_csa *csap, *csasp, *next_csasp;
+
+ /*
+ * If this isn't a CSU Request, or there's no outstanding CSUS,
+ * or the outstanding CSUS has already been satisfied, just
+ * return
+ */
+ if (!msg || msg->sc_msg_type != SCSP_CSU_REQ_MSG ||
+ !dcsp->sd_csus_rexmt_msg ||
+ !dcsp->sd_csus_rexmt_msg->sc_csu_msg ||
+ !dcsp->sd_csus_rexmt_msg->sc_csu_msg->csu_csa_rec)
+ return;
+
+
+ /*
+ * Loop through the CSASs in the CSUS message, checking for
+ * each in the CSA records of the received CSU Request
+ */
+ csusp = dcsp->sd_csus_rexmt_msg->sc_csu_msg;
+ for (csasp = csusp->csu_csa_rec; csasp; csasp = next_csasp) {
+ next_csasp = csasp->next;
+ for (csap = msg->sc_csu_msg->csu_csa_rec;
+ csap; csap = csap->next) {
+ /*
+ * If the records match, unlink and free the
+ * CSAS from the CSUS
+ */
+ if (scsp_cmp_key(&csap->key, &csasp->key) == 0 &&
+ scsp_cmp_key(&csap->key, &csasp->key) == 0 &&
+ scsp_cmp_id(&csap->oid, &csasp->oid) == 0 &&
+ csap->seq >= csasp->seq) {
+ UNLINK(csasp, Scsp_csa,
+ csusp->csu_csa_rec,
+ next);
+ SCSP_FREE_CSA(csasp);
+ dcsp->sd_csus_rexmt_msg->sc_csu_msg->csu_mcp.rec_cnt--;
+ break;
+ }
+ }
+ }
+
+ if (csusp->csu_csa_rec == (Scsp_csa *)0) {
+ /*
+ * All CSASs in the CSUS message have been
+ * answered. Stop the timer and free the
+ * saved message.
+ */
+ HARP_CANCEL(&dcsp->sd_csus_rexmt_t);
+ scsp_free_msg(dcsp->sd_csus_rexmt_msg);
+ dcsp->sd_csus_rexmt_msg = (Scsp_msg *)0;
+
+ /*
+ * If the CRL isn't empty, send another CSUS
+ */
+ if (dcsp->sd_crl) {
+ (void)scsp_send_csus(dcsp);
+ }
+ }
+}
+
+
+/*
+ * Send a CA message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ *
+ * Returns:
+ * 0 message sent OK
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_ca(dcsp)
+ Scsp_dcs *dcsp;
+{
+ int rc;
+ Scsp_msg *ca_msg;
+ Scsp_ca *cap;
+ Scsp_server *ssp = dcsp->sd_server;
+
+ /*
+ * Get memory for a CA message
+ */
+ ca_msg = calloc(1, sizeof(Scsp_msg));
+ if (ca_msg == NULL)
+ scsp_mem_err("scsp_send_ca: sizeof(Scsp_msg)");
+ cap = calloc(1, sizeof(Scsp_ca));
+ if (cap == NULL)
+ scsp_mem_err("scsp_send_ca: sizeof(Scsp_ca)");
+
+ /*
+ * Fill out constant fields
+ */
+ ca_msg->sc_msg_type = SCSP_CA_MSG;
+ ca_msg->sc_ca = cap;
+ cap->ca_seq = dcsp->sd_ca_seq;
+ cap->ca_mcp.pid = ssp->ss_pid;
+ cap->ca_mcp.sgid = ssp->ss_sgid;
+ cap->ca_mcp.sid = ssp->ss_lsid;
+ cap->ca_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Fill out state-dependent fields
+ */
+ switch(dcsp->sd_ca_state) {
+ case SCSP_CAFSM_NEG:
+ cap->ca_m = 1;
+ cap->ca_i = 1;
+ cap->ca_o = 1;
+ break;
+ case SCSP_CAFSM_MASTER:
+ cap->ca_m = 1;
+ cap->ca_i = 0;
+ scsp_ca_csas_setup(dcsp, cap);
+ cap->ca_o = dcsp->sd_ca_csas != (Scsp_cse *)0;
+ break;
+ case SCSP_CAFSM_SLAVE:
+ cap->ca_m = 0;
+ cap->ca_i = 0;
+ scsp_ca_csas_setup(dcsp, cap);
+ cap->ca_o = dcsp->sd_ca_csas != (Scsp_cse *)0;
+ break;
+ default:
+ scsp_log(LOG_ERR, "Invalid state in scsp_send_ca");
+ abort();
+ }
+
+ /*
+ * Send the CA message and save a pointer to it in case
+ * it needs to be retransmitted
+ */
+ rc = scsp_send_msg(dcsp, ca_msg);
+ if (rc == 0) {
+ dcsp->sd_ca_rexmt_msg = ca_msg;
+ } else {
+ scsp_free_msg(ca_msg);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Send a CSU Solicit message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ *
+ * Returns:
+ * 0 message sent OK
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_csus(dcsp)
+ Scsp_dcs *dcsp;
+{
+ int csas_len, len, mtu, rc;
+ Scsp_msg *csus_msg;
+ Scsp_csu_msg *csusp;
+ Scsp_csa *csasp, *next_csasp;
+ Scsp_server *ssp = dcsp->sd_server;
+
+ /*
+ * If we have a mesage saved for retransmission, use it.
+ * If not, get memory for a new one.
+ */
+ if (dcsp->sd_csus_rexmt_msg) {
+ csus_msg = dcsp->sd_csus_rexmt_msg;
+ csusp = csus_msg->sc_csu_msg;
+ } else {
+ /*
+ * Get memory for a CSUS message
+ */
+ csus_msg = calloc(1, sizeof(Scsp_msg));
+ if (csus_msg == NULL)
+ scsp_mem_err("scsp_send_csus: sizeof(Scsp_msg)");
+ csusp = calloc(1, sizeof(Scsp_csu_msg));
+ if (csusp == NULL)
+ scsp_mem_err("scsp_send_csus: sizeof(Scsp_csu_msg)");
+
+ /*
+ * Fill out constant fields
+ */
+ csus_msg->sc_msg_type = SCSP_CSUS_MSG;
+ csus_msg->sc_csu_msg = csusp;
+ csusp->csu_mcp.pid = ssp->ss_pid;
+ csusp->csu_mcp.sgid = ssp->ss_sgid;
+ csusp->csu_mcp.sid = ssp->ss_lsid;
+ csusp->csu_mcp.rid = dcsp->sd_dcsid;
+ }
+
+ /*
+ * Move CSAS records from CRL into message
+ */
+ mtu = dcsp->sd_server->ss_mtu;
+ csas_len = sizeof(struct scsp_ncsa) + ssp->ss_id_len +
+ ssp->ss_ckey_len;
+ len = sizeof(struct scsp_nhdr) + sizeof(struct scsp_nmcp) +
+ 2 * ssp->ss_id_len +
+ csas_len * (csusp->csu_mcp.rec_cnt + 1);
+ for (csasp = dcsp->sd_crl;
+ csasp && ((len + csas_len) < mtu);
+ csasp = next_csasp, len += csas_len) {
+ next_csasp = csasp->next;
+ csusp->csu_mcp.rec_cnt++;
+ UNLINK(csasp, Scsp_csa, dcsp->sd_crl, next);
+ LINK2TAIL(csasp, Scsp_csa, csusp->csu_csa_rec, next);
+ csasp->hops = 1;
+ }
+
+ /*
+ * Send the CSUS message and save a pointer to it in case
+ * it needs to be retransmitted
+ */
+ rc = scsp_send_msg(dcsp, csus_msg);
+ if (rc == 0) {
+ /*
+ * Success--Save a pointer to the message and
+ * start the CSUS retransmit timer
+ */
+ dcsp->sd_csus_rexmt_msg = csus_msg;
+ HARP_TIMER(&dcsp->sd_csus_rexmt_t,
+ dcsp->sd_csus_rexmt_int,
+ scsp_csus_retran_timeout);
+ } else {
+ /*
+ * Error--free the CSUS message
+ */
+ scsp_free_msg(csus_msg);
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Send a CSU Request message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ * csap pointer to CSAs to include
+ *
+ * Returns:
+ * 0 message sent OK
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_csu_req(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ int rc;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_csa *cnt_csap;
+ Scsp_msg *csu_msg;
+ Scsp_csu_msg *csup;
+ Scsp_csu_rexmt *rxp;
+
+ /*
+ * Return if CSA list is empty
+ */
+ if (!csap)
+ return(0);
+
+ /*
+ * Get memory for a CSU Req message
+ */
+ csu_msg = calloc(1, sizeof(Scsp_msg));
+ if (csu_msg == NULL)
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_msg)");
+ csup = calloc(1, sizeof(Scsp_csu_msg));
+ if (csup == NULL)
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_csu_msg)");
+
+ /*
+ * Get memory for a CSU Req retransmission queue entry
+ */
+ rxp = calloc(1, sizeof(Scsp_csu_rexmt));
+ if (rxp == NULL)
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_csu_rexmt)");
+
+ /*
+ * Fill out constant fields
+ */
+ csu_msg->sc_msg_type = SCSP_CSU_REQ_MSG;
+ csu_msg->sc_csu_msg = csup;
+ csup->csu_mcp.pid = ssp->ss_pid;
+ csup->csu_mcp.sgid = ssp->ss_sgid;
+ csup->csu_mcp.sid = ssp->ss_lsid;
+ csup->csu_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Put the CSA list into the message
+ */
+ csup->csu_csa_rec = csap;
+ for (cnt_csap = csap; cnt_csap; cnt_csap = cnt_csap->next) {
+ csup->csu_mcp.rec_cnt++;
+ }
+
+ /*
+ * Send the CSU Request
+ */
+ rc = scsp_send_msg(dcsp, csu_msg);
+ if (rc) {
+ scsp_free_msg(csu_msg);
+ return(rc);
+ }
+ free(csu_msg);
+ free(csup);
+
+ /*
+ * Save the CSA entries on the CSU Request retransmission
+ * queue and start the retransmission timer
+ */
+ rxp->sr_dcs = dcsp;
+ rxp->sr_csa = csap;
+ HARP_TIMER(&rxp->sr_t, dcsp->sd_csu_rexmt_int,
+ scsp_csu_req_retran_timeout);
+ LINK2TAIL(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt, sr_next);
+
+ return(0);
+}
+
+
+/*
+ * Send a CSU Reply message
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for DCS
+ * csap pointer to CSAs to include
+ *
+ * Returns:
+ * 0 message sent OK
+ * errno reason for failure
+ *
+ */
+int
+scsp_send_csu_reply(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ int rc;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_csa *csap1;
+ Scsp_msg *csu_msg;
+ Scsp_csu_msg *csup;
+
+ /*
+ * Return if CSA list is empty
+ */
+ if (!csap)
+ return(0);
+
+ /*
+ * Get memory for a CSU Reply message
+ */
+ csu_msg = calloc(1, sizeof(Scsp_msg));
+ if (csu_msg == NULL)
+ scsp_mem_err("scsp_send_csu_reply: sizeof(Scsp_msg)");
+ csup = calloc(1, sizeof(Scsp_csu_msg));
+ if (csup == NULL)
+ scsp_mem_err("scsp_send_csu_reply: sizeof(Scsp_csu_msg)");
+
+ /*
+ * Fill out constant fields
+ */
+ csu_msg->sc_msg_type = SCSP_CSU_REPLY_MSG;
+ csu_msg->sc_csu_msg = csup;
+ csup->csu_mcp.pid = ssp->ss_pid;
+ csup->csu_mcp.sgid = ssp->ss_sgid;
+ csup->csu_mcp.sid = ssp->ss_lsid;
+ csup->csu_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Put the CSA list into the message. Convert the CSAs into
+ * CSASs by freeing the protocol-specific portion.
+ */
+ csup->csu_csa_rec = csap;
+ for (csap1 = csap; csap1; csap1 = csap1->next) {
+ switch(dcsp->sd_server->ss_pid) {
+ /*
+ * We currently only support ATMARP
+ */
+ case SCSP_PROTO_ATMARP:
+ if (csap1->atmarp_data) {
+ free(csap1->atmarp_data);
+ csap1->atmarp_data =
+ (Scsp_atmarp_csa *)0;
+ }
+ break;
+ }
+ csup->csu_mcp.rec_cnt++;
+ }
+
+ /*
+ * Send the CSU Reply
+ */
+ rc = scsp_send_msg(dcsp, csu_msg);
+ scsp_free_msg(csu_msg);
+
+ return(rc);
+}
+
+
+/*
+ * Send a Hello message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_send_hello(dcsp)
+ Scsp_dcs *dcsp;
+{
+ int rc;
+ Scsp_msg *hello;
+ Scsp_hello *hp;
+
+ /*
+ * Get memory for a Hello message
+ */
+ hello = calloc(1, sizeof(Scsp_msg));
+ if (hello == NULL)
+ scsp_mem_err("scsp_send_hello: sizeof(Scsp_msg)");
+ hp = calloc(1, sizeof(Scsp_hello));
+ if (hp == NULL)
+ scsp_mem_err("scsp_send_hello: sizeof(Scsp_hello)");
+
+ /*
+ * Set up the Hello message
+ */
+ hello->sc_msg_type = SCSP_HELLO_MSG;
+ hello->sc_hello = hp;
+ hp->hello_int = SCSP_HELLO_Interval;
+ hp->dead_factor = SCSP_HELLO_DF;
+ hp->family_id = dcsp->sd_server->ss_fid;
+ hp->hello_mcp.pid = dcsp->sd_server->ss_pid;
+ hp->hello_mcp.sgid = dcsp->sd_server->ss_sgid;
+ hp->hello_mcp.flags = 0;
+ hp->hello_mcp.rec_cnt = 0;
+ hp->hello_mcp.sid = dcsp->sd_server->ss_lsid;
+ hp->hello_mcp.rid = dcsp->sd_dcsid;
+
+ /*
+ * Send and free the message
+ */
+ rc = scsp_send_msg(dcsp, hello);
+ scsp_free_msg(hello);
+
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_msg.h b/usr.sbin/atm/scspd/scsp_msg.h
new file mode 100644
index 0000000..bedea8a
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_msg.h
@@ -0,0 +1,461 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP message formats
+ *
+ */
+
+#ifndef _SCSP_SCSP_MSG_H
+#define _SCSP_SCSP_MSG_H
+
+
+/*
+ * ATMARP constants
+ */
+#define ARP_ATMFORUM 19
+#define ARP_TL_TMASK 0x40 /* Type mask */
+#define ARP_TL_NSAPA 0x00 /* Type = ATM Forum NSAPA */
+#define ARP_TL_E164 0x40 /* Type = E.164 */
+#define ARP_TL_LMASK 0x3f /* Length mask */
+
+
+/*
+ * SCSP version number
+ */
+#define SCSP_VER_1 1
+
+
+/*
+ * SCSP message types
+ */
+#define SCSP_CA_MSG 1
+#define SCSP_CSU_REQ_MSG 2
+#define SCSP_CSU_REPLY_MSG 3
+#define SCSP_CSUS_MSG 4
+#define SCSP_HELLO_MSG 5
+
+
+/*
+ * SCSP Client Protocol IDs
+ */
+#define SCSP_PROTO_ATMARP 1
+#define SCSP_PROTO_NHRP 2
+#define SCSP_PROTO_MARS 3
+#define SCSP_PROTO_DHCP 4
+#define SCSP_PROTO_LNNI 5
+
+
+/*
+ * Extension types
+ */
+#define SCSP_EXT_END 0
+#define SCSP_EXT_AUTH 1
+#define SCSP_EXT_VENDOR 2
+
+/*
+ * Sequence number bounds
+ */
+#define SCSP_CSA_SEQ_MIN 0x80000001
+#define SCSP_CSA_SEQ_MAX 0x7FFFFFFF
+
+
+/*
+ * Sender, Receiver, or Originator ID lengths
+ */
+#define SCSP_ATMARP_ID_LEN 4
+#define SCSP_NHRP_ID_LEN 4
+#define SCSP_MAX_ID_LEN 4
+
+
+/*
+ * Cache Key lengths
+ */
+#define SCSP_ATMARP_KEY_LEN 4
+#define SCSP_NHRP_KEY_LEN 4
+#define SCSP_MAX_KEY_LEN 4
+
+
+/*
+ * Fixed header
+ */
+struct scsp_nhdr {
+ u_char sh_ver; /* SCSP version */
+ u_char sh_type; /* Message type */
+ u_short sh_len; /* Message length */
+ u_short sh_checksum; /* IP checksum over message */
+ u_short sh_ext_off; /* Offset of first extension */
+};
+
+
+/*
+ * Mandatory common part
+ */
+struct scsp_nmcp {
+ u_short sm_pid; /* Protocol ID */
+ u_short sm_sgid; /* Server group ID */
+ u_short sm_fill_0; /* Unused */
+ u_short sm_flags; /* Flags--see below */
+ u_char sm_sid_len; /* Sender ID length */
+ u_char sm_rid_len; /* Receiver ID length */
+ u_short sm_rec_cnt; /* Number of records */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char sm_sid[]; /* Sender ID (variable) */
+ u_char sm_rid[]; /* Receiver ID (variable) */
+#endif
+};
+
+
+/*
+ * Extensions part
+ */
+struct scsp_next {
+ u_short se_type; /* Extension type */
+ u_short se_len; /* Length */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char se_value[]; /* Extension value */
+#endif
+};
+
+
+/*
+ * Cache State Advertisement record or
+ * Cache State Advertisement Summary record
+ */
+struct scsp_ncsa {
+ u_short scs_hop_cnt; /* Hop count */
+ u_short scs_len; /* Record length */
+ u_char scs_ck_len; /* Cache key length */
+ u_char scs_oid_len; /* Originator ID length */
+ u_short scs_nfill; /* Null bit and filler */
+ long scs_seq; /* Sequence number */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char scs_ckey[]; /* Cache key */
+ u_char scs_oid[]; /* Originator ID */
+ u_char scs_proto[]; /* Protocol-specific (in CSA) */
+#endif
+};
+
+#define SCSP_CSAS_NULL 0x8000
+
+
+/*
+ * Cache Alignment message
+ */
+struct scsp_nca {
+ long sca_seq; /* Sequence number */
+ struct scsp_nmcp sca_mcp; /* Mandatory common */
+#ifdef NOTDEF
+ /* Variable length fields */
+ struct scsp_ncsa sca_rec[]; /* CSASs */
+#endif
+};
+
+#define SCSP_CA_M 0x8000 /* Master/Slave bit */
+#define SCSP_CA_I 0x4000 /* Initialization bit */
+#define SCSP_CA_O 0x2000 /* More bit */
+
+
+/*
+ * Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message
+ */
+struct scsp_ncsu_msg {
+ struct scsp_nmcp scr_mcp; /* Mandatory common */
+#ifdef NOTDEF
+ /* Variable length fields */
+ struct scsp_ncsa scr_rec[]; /* CSAs */
+#endif
+};
+
+
+/*
+ * Hello message
+ */
+struct scsp_nhello {
+ u_short sch_hi; /* Hello interval */
+ u_short sch_df; /* Dead factor */
+ u_short sch_fill_0; /* Unused */
+ u_short sch_fid; /* Family ID */
+ struct scsp_nmcp sch_mcp; /* Mandatory common */
+#ifdef NOTDEF
+ /* Variable-length fields */
+ struct scsp_nrid sch_rid[]; /* Receiver IDs */
+#endif
+};
+
+
+/*
+ * ATMARP-specific Cache State Advertisement record
+ */
+struct scsp_atmarp_ncsa {
+ u_short sa_hrd; /* Hardware type -- 0x0013 */
+ u_short sa_pro; /* Protocol type -- 0x0800 */
+ u_char sa_shtl; /* Src ATM addr type/len */
+ u_char sa_sstl; /* Src ATM subaddr type/len */
+ u_char sa_state; /* State */
+ u_char sa_fill1; /* Unused */
+ u_char sa_spln; /* Src proto addr type */
+ u_char sa_thtl; /* Tgt ATM addr type/len */
+ u_char sa_tstl; /* Tgt ATM subaddr type/len */
+ u_char sa_tpln; /* Tgt proto addr len */
+#ifdef NOTDEF
+ /* Variable-length fields */
+ u_char sa_sha[]; /* Source ATM addr */
+ u_char sa_ssa[]; /* Source ATM subaddr */
+ u_char sa_spa[]; /* Source IP addr */
+ u_char sa_tha[]; /* Target ATM addr */
+ u_char sa_tsa[]; /* Target ATM subaddr */
+ u_char sa_tpa[]; /* Target IP addr */
+#endif
+};
+
+
+/*
+ * NHRP-specific Cache State Advertisement record
+ */
+struct scsp_nhrp_ncsa {
+ u_short sn_af; /* Address family */
+ u_short sn_pro; /* NHRP protocol type */
+ u_char sn_snap[5]; /* SNAP header */
+ u_char sn_ver; /* NHRP version no. */
+ u_short sn_flags; /* Flags */
+ u_long sn_rid; /* Request ID */
+ u_char sn_state; /* State */
+ u_char sn_pln; /* Prefix length */
+ u_short sn_fill1; /* Unused */
+ u_short sn_mtu; /* MTU */
+ u_short sn_hold; /* Holding time */
+ u_char sn_csatl; /* Client addr type/len */
+ u_char sn_csstl; /* Client subaddr type/len */
+ u_char sn_cpln; /* Client proto addr len */
+ u_char sn_pref; /* Preference for next hop */
+#ifdef NOTDEF
+ /* Variable-length fields */
+ u_char sn_csa[]; /* Client subnetwork addr */
+ u_char sn_css[]; /* Client subnetwork subaddr */
+ u_char sn_cpa[]; /* Client protocol addr */
+#endif
+};
+
+
+/*
+ * SCSP messages in internal format
+ *
+ *
+ * Fixed message header
+ */
+struct scsp_hdr {
+ u_char msg_type; /* Message type */
+};
+typedef struct scsp_hdr Scsp_hdr;
+
+
+/*
+ * Sender or Receiver ID structure
+ */
+struct scsp_id {
+ struct scsp_id *next; /* Next ID */
+ u_char id_len; /* ID length */
+ u_char id[SCSP_MAX_ID_LEN]; /* ID */
+};
+typedef struct scsp_id Scsp_id;
+
+
+/*
+ * Cacke Key structure
+ */
+struct scsp_ckey {
+ u_char key_len; /* Cache key length */
+ u_char key[SCSP_MAX_KEY_LEN]; /* Cache key */
+};
+typedef struct scsp_ckey Scsp_ckey;
+
+
+/*
+ * Mandatory common part
+ */
+struct scsp_mcp {
+ u_short pid; /* Protocol ID */
+ u_short sgid; /* Server group ID */
+ u_short flags; /* Flags */
+ u_short rec_cnt; /* No. of records attached */
+ Scsp_id sid; /* Sender ID */
+ Scsp_id rid; /* Receiver ID */
+};
+typedef struct scsp_mcp Scsp_mcp;
+
+
+/*
+ * Extensions part
+ */
+struct scsp_ext {
+ struct scsp_ext *next; /* Next extension */
+ u_short type; /* Extension type */
+ u_short len; /* Length */
+#ifdef NOTDEF
+ /* Variable length fields */
+ u_char value[]; /* Extension value */
+#endif
+};
+typedef struct scsp_ext Scsp_ext;
+
+
+/*
+ * Cache State Advertisement record or
+ * Cache State Advertisement Summary record
+ */
+struct scsp_csa {
+ struct scsp_csa *next; /* Next CSAS record */
+ u_short hops; /* Hop count */
+ u_char null; /* Null flag */
+ u_long seq; /* CSA seq. no. */
+ Scsp_ckey key; /* Cache key */
+ Scsp_id oid; /* Originator ID */
+ int trans_ct; /* No. of times CSA sent */
+ struct scsp_atmarp_csa *atmarp_data; /* ATMARP data */
+#ifdef NOTDEF
+ struct scsp_nhrp_csa *nhrp_data; /* NHRP data */
+#endif
+};
+typedef struct scsp_csa Scsp_csa;
+
+/*
+ * Macro to free a CSA and any associated protocol-specific data
+ */
+#define SCSP_FREE_CSA(c) \
+{ \
+ if ((c)->atmarp_data) \
+ free((c)->atmarp_data); \
+ free((c)); \
+}
+
+
+/*
+ * Cache Alignment message
+ */
+struct scsp_ca {
+ long ca_seq; /* CA msg sequence no. */
+ u_char ca_m; /* Master/slave bit */
+ u_char ca_i; /* Initialization bit */
+ u_char ca_o; /* More bit */
+ Scsp_mcp ca_mcp; /* Mandatory common part */
+ Scsp_csa *ca_csa_rec; /* Ptr. to CSAS records */
+};
+typedef struct scsp_ca Scsp_ca;
+
+
+/*
+ * Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message
+ */
+struct scsp_csu_msg {
+ Scsp_mcp csu_mcp; /* Mandatory common part */
+ Scsp_csa *csu_csa_rec; /* Ptr. to CSA records */
+};
+typedef struct scsp_csu_msg Scsp_csu_msg;
+
+
+/*
+ * Hello message
+ */
+struct scsp_hello {
+ u_short hello_int; /* Hello interval */
+ u_short dead_factor; /* When is DCS dead? */
+ u_short family_id; /* Family ID */
+ Scsp_mcp hello_mcp; /* Mandatory common part */
+};
+typedef struct scsp_hello Scsp_hello;
+
+
+/*
+ * NHRP-specific Cache State Advertisement record
+ */
+struct scsp_nhrp_csa {
+ u_char req_id; /* Request ID */
+ u_char state; /* State */
+ u_char pref_len; /* Prefix length */
+ u_short flags; /* See below */
+ u_short mtu; /* Maximim transmission unit */
+ u_short hold_time; /* Entry holding time */
+ u_char caddr_tlen; /* Client addr type/length */
+ u_char csaddr_tlen; /* Client subaddr type/length */
+ u_char cproto_len; /* Client proto addr length */
+ u_char pref; /* Preference */
+ Atm_addr caddr; /* Client address */
+ Atm_addr csaddr; /* Client subaddress */
+ struct in_addr cproto_addr; /* Client protocol address */
+};
+typedef struct scsp_nhrp Scsp_nhrp;
+
+#define SCSP_NHRP_UNIQ 0x8000
+#define SCSP_NHRP_ARP 0x4000
+
+
+/*
+ * ATMARP-specific Cache State Advertisement record
+ */
+struct scsp_atmarp_csa {
+ u_char sa_state; /* State */
+ Atm_addr sa_sha; /* Source ATM addr */
+ Atm_addr sa_ssa; /* Source ATM subaddr */
+ struct in_addr sa_spa; /* Source IP addr */
+ Atm_addr sa_tha; /* Target ATM addr */
+ Atm_addr sa_tsa; /* Target ATM subaddr */
+ struct in_addr sa_tpa; /* Target IP addr */
+};
+typedef struct scsp_atmarp_csa Scsp_atmarp_csa;
+
+
+/*
+ * SCSP message
+ */
+struct scsp_msg {
+ Scsp_hdr sc_hdr;
+ union {
+ Scsp_ca *sc_u_ca;
+ Scsp_csu_msg *sc_u_csu_msg;
+ Scsp_hello *sc_u_hello;
+ } sc_msg_u;
+ Scsp_ext *sc_ext;
+};
+typedef struct scsp_msg Scsp_msg;
+
+#define sc_msg_type sc_hdr.msg_type
+#define sc_ca sc_msg_u.sc_u_ca
+#define sc_csu_msg sc_msg_u.sc_u_csu_msg
+#define sc_hello sc_msg_u.sc_u_hello
+
+#endif /* _SCSP_SCSP_MSG_H */
diff --git a/usr.sbin/atm/scspd/scsp_output.c b/usr.sbin/atm/scspd/scsp_output.c
new file mode 100644
index 0000000..c2ea657
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_output.c
@@ -0,0 +1,930 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Output packet processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Put a long integer into the output buffer
+ *
+ * This routine is provided for cases where long ints may not be
+ * word-aligned in the output buffer.
+ *
+ * Arguments:
+ * l long integer
+ * cp pointer to output buffer
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+put_long(l, cp)
+ u_long l;
+ u_char *cp;
+{
+ u_long nl;
+
+ /*
+ * Convert to network order and copy to output buffer
+ */
+ nl = htonl(l);
+ bcopy(&nl, cp, sizeof(u_long));
+}
+
+
+/*
+ * Format a Sender or Receiver ID
+ *
+ * Arguments:
+ * idp ponter to ID structure
+ * buff pointer to ID
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of ID processed
+ *
+ */
+static int
+scsp_format_id(idp, buff)
+ Scsp_id *idp;
+ char *buff;
+{
+ /*
+ * Copy the ID
+ */
+ bcopy(idp->id, buff, idp->id_len);
+
+ /*
+ * Return the ID length
+ */
+ return(idp->id_len);
+}
+
+
+/*
+ * Format the Mandatory Common Part of an SCSP input packet
+ *
+ * Arguments:
+ * mcp pointer to MCP
+ * buff pointer to mandatory common part
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of MCP in message
+ *
+ */
+static int
+scsp_format_mcp(mcp, buff)
+ Scsp_mcp *mcp;
+ char *buff;
+{
+ int len;
+ char *odp;
+ struct scsp_nmcp *smp;
+
+ /*
+ * Set the protocol ID
+ */
+ smp = (struct scsp_nmcp *)buff;
+ smp->sm_pid = htons(mcp->pid);
+
+ /*
+ * Set the server group ID
+ */
+ smp->sm_sgid = htons(mcp->sgid);
+
+ /*
+ * Set the flags
+ */
+ smp->sm_flags = htons(mcp->flags);
+
+ /*
+ * Set the sender ID and length
+ */
+ smp->sm_sid_len = mcp->sid.id_len;
+ odp = buff + sizeof(struct scsp_nmcp);
+ len = scsp_format_id(&mcp->sid, odp);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Set the receiver ID and length
+ */
+ smp->sm_rid_len = mcp->rid.id_len;
+ odp += mcp->sid.id_len;
+ len = scsp_format_id(&mcp->rid, odp);
+ if (len == 0) {
+ goto mcp_invalid;
+ }
+
+ /*
+ * Set the record count
+ */
+ smp->sm_rec_cnt = htons(mcp->rec_cnt);
+
+ /*
+ * Return the length of data we processed
+ */
+ return(sizeof(struct scsp_nmcp) + mcp->sid.id_len +
+ mcp->rid.id_len);
+
+mcp_invalid:
+ return(0);
+}
+
+
+/*
+ * Format an Extension
+ *
+ * Arguments:
+ * exp pointer to extension in internal format
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of extension processed
+ *
+ */
+static int
+scsp_format_ext(exp, buff, blen)
+ Scsp_ext *exp;
+ char *buff;
+ int blen;
+{
+ struct scsp_next *sep;
+
+ /*
+ * Make sure there's room in the buffer
+ */
+ if (blen < (sizeof(struct scsp_next) + exp->len))
+ return(0);
+
+ /*
+ * Set the type
+ */
+ sep = (struct scsp_next *)buff;
+ sep->se_type = htons(exp->type);
+
+ /*
+ * Set the length
+ */
+ sep->se_len = htons(exp->len);
+
+ /*
+ * Set the value
+ */
+ if (exp->len > 0) {
+ buff += sizeof(struct scsp_next);
+ bcopy((caddr_t)exp + sizeof(Scsp_ext),
+ buff,
+ exp->len);
+ }
+
+ /*
+ * Return the number of bytes processed
+ */
+ return(sizeof(struct scsp_next) + exp->len);
+}
+
+
+/*
+ * Format the ATMARP part of a CSA record
+ *
+ * Arguments:
+ * acsp pointer to ATMARP protocol-specific CSA record
+ * buff pointer to output buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of record processed
+ *
+ */
+static int
+scsp_format_atmarp(acsp, buff)
+ Scsp_atmarp_csa *acsp;
+ char *buff;
+{
+ char *cp;
+ int len, pkt_len;
+ struct scsp_atmarp_ncsa *sanp;
+
+ /*
+ * Figure out how long PDU is going to be
+ */
+ pkt_len = sizeof(struct scsp_atmarp_ncsa);
+ switch (acsp->sa_sha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ pkt_len += acsp->sa_sha.address_length;
+ break;
+
+ case T_ATM_E164_ADDR:
+ pkt_len += acsp->sa_sha.address_length;
+ if (acsp->sa_ssa.address_format == T_ATM_ENDSYS_ADDR)
+ pkt_len += acsp->sa_ssa.address_length;
+ break;
+ }
+
+ switch (acsp->sa_tha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ pkt_len += acsp->sa_tha.address_length;
+ break;
+
+ case T_ATM_E164_ADDR:
+ pkt_len += acsp->sa_tha.address_length;
+ if (acsp->sa_tha.address_format == T_ATM_ENDSYS_ADDR)
+ pkt_len += acsp->sa_tha.address_length;
+ break;
+ }
+
+ if (acsp->sa_spa.s_addr != 0)
+ pkt_len += sizeof(struct in_addr);
+
+ if (acsp->sa_tpa.s_addr != 0)
+ pkt_len += sizeof(struct in_addr);
+
+ /*
+ * Set up pointers
+ */
+ sanp = (struct scsp_atmarp_ncsa *)buff;
+ cp = (char *)sanp + sizeof(struct scsp_atmarp_ncsa);
+
+ /*
+ * Build fields
+ */
+ sanp->sa_hrd = htons(ARP_ATMFORUM);
+ sanp->sa_pro = htons(ETHERTYPE_IP);
+
+ /* sa_sha */
+ len = acsp->sa_sha.address_length;
+ switch (acsp->sa_sha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ sanp->sa_shtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK);
+
+ /* sa_sha */
+ bcopy(acsp->sa_sha.address, cp, len);
+ cp += len;
+
+ sanp->sa_sstl = 0;
+ break;
+
+ case T_ATM_E164_ADDR:
+ sanp->sa_shtl = ARP_TL_E164 | (len & ARP_TL_LMASK);
+
+ /* sa_sha */
+ bcopy(acsp->sa_sha.address, cp, len);
+ cp += len;
+
+ if (acsp->sa_ssa.address_format == T_ATM_ENDSYS_ADDR) {
+ len = acsp->sa_ssa.address_length;
+ sanp->sa_sstl = ARP_TL_NSAPA |
+ (len & ARP_TL_LMASK);
+
+ /* sa_ssa */
+ bcopy(acsp->sa_ssa.address, cp, len);
+ cp += len;
+ } else
+ sanp->sa_sstl = 0;
+ break;
+
+ default:
+ sanp->sa_shtl = 0;
+ sanp->sa_sstl = 0;
+ }
+
+ /* sa_state */
+ sanp->sa_state = acsp->sa_state;
+ sanp->sa_fill1 = 0;
+
+ /* sa_spa */
+ if (acsp->sa_spa.s_addr != 0) {
+ sanp->sa_spln = sizeof(struct in_addr);
+ bcopy(&acsp->sa_spa, cp, sizeof(struct in_addr));
+ cp += sizeof(struct in_addr);
+ }
+
+ /* sa_tha */
+ len = acsp->sa_tha.address_length;
+ switch (acsp->sa_tha.address_format) {
+ case T_ATM_ENDSYS_ADDR:
+ sanp->sa_thtl = ARP_TL_NSAPA | (len & ARP_TL_LMASK);
+
+ /* sa_tha */
+ bcopy(acsp->sa_tha.address, cp, len);
+ cp += len;
+
+ sanp->sa_tstl = 0;
+ break;
+
+ case T_ATM_E164_ADDR:
+ sanp->sa_thtl = ARP_TL_E164 | (len & ARP_TL_LMASK);
+
+ /* sa_tha */
+ bcopy(acsp->sa_tha.address, cp, len);
+ cp += len;
+
+ if (acsp->sa_tsa.address_format == T_ATM_ENDSYS_ADDR) {
+ len = acsp->sa_tha.address_length;
+ sanp->sa_tstl = ARP_TL_NSAPA |
+ (len & ARP_TL_LMASK);
+
+ /* sa_tsa */
+ bcopy(acsp->sa_tsa.address, cp, len);
+ cp += len;
+ } else
+ sanp->sa_tstl = 0;
+ break;
+
+ default:
+ sanp->sa_thtl = 0;
+ sanp->sa_tstl = 0;
+ }
+
+ /* sa_tpa */
+ if (acsp->sa_tpa.s_addr != 0) {
+ sanp->sa_tpln = sizeof(struct in_addr);
+ bcopy(&acsp->sa_tpa, cp, sizeof(struct in_addr));
+ }
+
+ return(pkt_len);
+}
+
+
+/*
+ * Format a Cache State Advertisement or Cache State Advertisement
+ * Summary record
+ *
+ * Arguments:
+ * csapp pointer to CSA or CSAS
+ * buff pointer to output buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of record processed
+ *
+ */
+static int
+scsp_format_csa(csap, buff)
+ Scsp_csa *csap;
+ char *buff;
+{
+ int len = 0;
+ char *odp;
+ struct scsp_ncsa *scp;
+
+ /*
+ * Set the hop count
+ */
+ scp = (struct scsp_ncsa *)buff;
+ scp->scs_hop_cnt = htons(csap->hops);
+
+ /*
+ * Set the null flag
+ */
+ if (csap->null) {
+ scp->scs_nfill = htons(SCSP_CSAS_NULL);
+ }
+
+ /*
+ * Set the sequence number
+ */
+ put_long(csap->seq, (u_char *)&scp->scs_seq);
+
+ /*
+ * Set the cache key
+ */
+ scp->scs_ck_len = csap->key.key_len;
+ odp = buff + sizeof(struct scsp_ncsa);
+ bcopy(csap->key.key, odp, scp->scs_ck_len);
+
+ /*
+ * Set the originator ID
+ */
+ odp += scp->scs_ck_len;
+ scp->scs_oid_len = scsp_format_id(&csap->oid, odp);
+
+ /*
+ * Set the protocol-specific data, if present. At the
+ * moment, we only handle data for ATMARP.
+ */
+ if (csap->atmarp_data) {
+ odp += scp->scs_oid_len;
+ len = scsp_format_atmarp(csap->atmarp_data, odp);
+ }
+
+ /*
+ * Set the record length
+ */
+ scp->scs_len = htons(sizeof(struct scsp_ncsa) +
+ scp->scs_ck_len + scp->scs_oid_len +
+ len);
+
+ /*
+ * Return the length of data we processed
+ */
+ return(ntohs(scp->scs_len));
+}
+
+
+/*
+ * Format a Cache Alignment message
+ *
+ * Arguments:
+ * cap pointer to CA message
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CA message processed
+ *
+ */
+static int
+scsp_format_ca(cap, buff, blen)
+ Scsp_ca *cap;
+ char *buff;
+ int blen;
+{
+ int i, len, proc_len;
+ struct scsp_nca *scap;
+ Scsp_csa *csap;
+
+ /*
+ * Set the sequence number
+ */
+ scap = (struct scsp_nca *)buff;
+ put_long(cap->ca_seq, (u_char *)&scap->sca_seq);
+ proc_len = sizeof(scap->sca_seq);
+ buff += sizeof(scap->sca_seq);
+
+ /*
+ * Set the flags
+ */
+ cap->ca_mcp.flags = 0;
+ if (cap->ca_m)
+ cap->ca_mcp.flags |= SCSP_CA_M;
+ if (cap->ca_i)
+ cap->ca_mcp.flags |= SCSP_CA_I;
+ if (cap->ca_o)
+ cap->ca_mcp.flags |= SCSP_CA_O;
+
+ /*
+ * Format the mandatory common part of the message
+ */
+ len = scsp_format_mcp(&cap->ca_mcp, buff);
+ if (len == 0)
+ goto ca_invalid;
+ buff += len;
+ proc_len += len;
+
+ /*
+ * Put any CSAS records into the message
+ */
+ for (i = 0, csap = cap->ca_csa_rec; i < cap->ca_mcp.rec_cnt;
+ i++, csap = csap->next) {
+ len = scsp_format_csa(csap, buff);
+ buff += len;
+ proc_len += len;
+ if (proc_len > blen) {
+ scsp_log(LOG_CRIT, "scsp_format_ca: buffer overflow");
+ abort();
+ }
+ }
+
+ /*
+ * Return the length of processed data
+ */
+ return(proc_len);
+
+ca_invalid:
+ return(0);
+}
+
+
+/*
+ * Format a Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message. These all have the same format,
+ * a Mandatory Common Part followed by a number of CSA or CSAS records.
+ *
+ * Arguments:
+ * csup pointer to location to put pointer to CSU Req message
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of CSU Req message processed
+ *
+ */
+static int
+scsp_format_csu(csup, buff, blen)
+ Scsp_csu_msg *csup;
+ char *buff;
+ int blen;
+{
+ int i, len, proc_len;
+ struct scsp_ncsu_msg *scsup;
+ Scsp_csa *csap;
+
+ /*
+ * Format the mandatory common part of the message
+ */
+ scsup = (struct scsp_ncsu_msg *)buff;
+ len = scsp_format_mcp(&csup->csu_mcp, buff);
+ if (len == 0)
+ goto csu_invalid;
+ buff += len;
+ proc_len = len;
+
+ /*
+ * Put the CSAS records into the message
+ */
+ for (i = 0, csap = csup->csu_csa_rec;
+ i < csup->csu_mcp.rec_cnt && csap;
+ i++, csap = csap->next) {
+ len = scsp_format_csa(csap, buff);
+ buff += len;
+ proc_len += len;
+ if (proc_len > blen) {
+ scsp_log(LOG_CRIT, "scsp_format_csu: buffer overflow");
+ abort();
+ }
+ }
+
+ /*
+ * Return the length of processed data
+ */
+ return(proc_len);
+
+csu_invalid:
+ return(0);
+}
+
+
+/*
+ * Format a Hello message
+ *
+ * Arguments:
+ * hpp pointer to Hello message
+ * buff pointer to output buffer
+ * blen space available in buffer
+ *
+ * Returns:
+ * 0 input was invalid
+ * else length of Hello message processed
+ *
+ */
+static int
+scsp_format_hello(hp, buff, blen)
+ Scsp_hello *hp;
+ char *buff;
+ int blen;
+{
+ int len, proc_len;
+ struct scsp_nhello *shp;
+ Scsp_id *ridp;
+
+ /*
+ * Set the hello interval
+ */
+ shp = (struct scsp_nhello *)buff;
+ shp->sch_hi = htons(hp->hello_int);
+
+ /*
+ * Set the dead factor
+ */
+ shp->sch_df = htons(hp->dead_factor);
+
+ /*
+ * Set the family ID
+ */
+ shp->sch_fid = htons(hp->family_id);
+
+ /*
+ * Process the mandatory common part of the message
+ */
+ proc_len = sizeof(struct scsp_nhello) -
+ sizeof(struct scsp_nmcp);
+ buff += proc_len;
+ len = scsp_format_mcp(&hp->hello_mcp, buff);
+ if (len == 0)
+ goto hello_invalid;
+ proc_len += len;
+ buff += len;
+
+ /*
+ * Add any additional receiver ID records to the message
+ */
+ for (ridp = hp->hello_mcp.rid.next; ridp;
+ ridp = ridp->next) {
+ len = scsp_format_id(ridp, buff);
+ if (len == 0) {
+ goto hello_invalid;
+ }
+ proc_len += len;
+ buff += len;
+ }
+
+ /*
+ * Return the length of the Hello message body
+ */
+ if (proc_len > blen) {
+ scsp_log(LOG_CRIT, "scsp_format_hello: buffer overflow");
+ abort();
+ }
+ return(proc_len);
+
+hello_invalid:
+ return(0);
+}
+
+
+/*
+ * Format an SCSP output packet
+ *
+ * Arguments:
+ * dcsp pointer to DCS for which message is being prepared
+ * msg pointer to input packet
+ * bpp pointer to location to put pointer to formatted packet
+ *
+ * Returns:
+ * 0 input packet was invalid
+ * else length of formatted packet
+ *
+ */
+int
+scsp_format_msg(dcsp, msg, bpp)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+ char **bpp;
+{
+ char *buff = (char *)0, *e_buff = (char *)0;
+ int buff_len, e_buff_len;
+ int e_len, len, plen;
+ struct scsp_nhdr *shp;
+ Scsp_ext *exp;
+
+ /*
+ * Allocate a buffer for the message
+ */
+ buff_len = dcsp->sd_server->ss_mtu;
+ buff = calloc(1, buff_len);
+ if (buff == NULL)
+ scsp_mem_err("scsp_format_msg: dcsp->sd_server->ss_mtu");
+ *bpp = buff;
+
+ /*
+ * Encode the fixed header
+ *
+ * Set the version
+ */
+ shp = (struct scsp_nhdr *)buff;
+ shp->sh_ver = SCSP_VER_1;
+
+ /*
+ * Set the message type
+ */
+ shp->sh_type = msg->sc_msg_type;
+
+ /*
+ * Point past the fixed header
+ */
+ len = sizeof(struct scsp_nhdr);
+ buff_len -= len;
+
+ /*
+ * Encode any extensions into a temporary buffer
+ */
+ e_len = 0;
+ if (msg->sc_ext) {
+ /*
+ * Get a buffer for the extensions
+ */
+ e_buff_len = 1024;
+ e_buff = calloc(1, e_buff_len);
+ if (buff)
+ scsp_mem_err("scsp_format_msg: e_buff_len");
+
+ /*
+ * Encode the extensions
+ */
+ for (exp = msg->sc_ext = 0; exp; exp = exp->next) {
+ plen = scsp_format_ext(exp, e_buff + e_len,
+ e_buff_len - e_len);
+ if (plen == 0) {
+ goto ignore;
+ }
+ e_len += plen;
+ }
+
+ /*
+ * Free the buffer if we didn't use it
+ */
+ if (!e_len) {
+ free(e_buff);
+ e_buff = (char *)0;
+ }
+ }
+ buff_len -= e_len;
+
+ /*
+ * Encode the body of the message, depending on the type
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ plen = scsp_format_ca(msg->sc_ca, buff + len, buff_len);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ plen = scsp_format_csu(msg->sc_csu_msg, buff + len,
+ buff_len);
+ break;
+ case SCSP_HELLO_MSG:
+ plen = scsp_format_hello(msg->sc_hello, buff + len,
+ buff_len);
+ break;
+ default:
+ goto ignore;
+ }
+ if (plen == 0) {
+ goto ignore;
+ }
+ len += plen;
+
+ /*
+ * Copy the extensions to the end of the message
+ */
+ if (e_len) {
+ shp->sh_ext_off = htons(len);
+ bcopy(e_buff, buff + len, e_len);
+ free(e_buff);
+ }
+
+ /*
+ * Set the length
+ */
+ shp->sh_len = htons(len);
+
+ /*
+ * Compute the message checksum
+ */
+ shp->sh_checksum = htons(ip_checksum(buff, len));
+
+ /*
+ * Return the length of the buffer
+ */
+ return(len);
+
+ignore:
+ if (buff)
+ free(buff);
+ if (e_buff)
+ free(e_buff);
+ *bpp = (char *)0;
+ return(0);
+}
+
+
+/*
+ * Send an SCSP message
+ *
+ * Arguments:
+ * dcsp pointer to DCS control block
+ * msg pointer to message to send
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_send_msg(dcsp, msg)
+ Scsp_dcs *dcsp;
+ Scsp_msg *msg;
+{
+ int len, rc;
+ char *buff;
+
+ /*
+ * Make sure we have a socket open
+ */
+ if (dcsp->sd_sock == -1) {
+ return(EBADF);
+ }
+
+ /*
+ * Trace the message
+ */
+ if (((scsp_trace_mode & SCSP_TRACE_HELLO_MSG) &&
+ msg->sc_msg_type == SCSP_HELLO_MSG) ||
+ ((scsp_trace_mode & SCSP_TRACE_CA_MSG) &&
+ msg->sc_msg_type != SCSP_HELLO_MSG)) {
+ scsp_trace_msg(dcsp, msg, 0);
+ scsp_trace("\n");
+ }
+
+ /*
+ * Put the message into network format
+ */
+ len = scsp_format_msg(dcsp, msg, &buff);
+ if (len == 0) {
+ scsp_log(LOG_ERR, "scsp_send_msg: message conversion failed\n");
+ abort();
+ }
+
+ /*
+ * Write the message to the DCS
+ */
+ rc = write(dcsp->sd_sock, (void *)buff, len);
+ free(buff);
+ if (rc == len || (rc == -1 && errno == EINPROGRESS)) {
+ rc = 0;
+ } else {
+ /*
+ * There was an error on the write--close the VCC
+ */
+ (void)close(dcsp->sd_sock);
+ dcsp->sd_sock = -1;
+
+ /*
+ * Inform the Hello FSM
+ */
+ (void)scsp_hfsm(dcsp, SCSP_HFSM_VC_CLOSED,
+ (Scsp_msg *)0);
+
+ /*
+ * Set the return code
+ */
+ if (rc == -1)
+ rc = errno;
+ else
+ rc = EINVAL;
+ }
+
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_print.c b/usr.sbin/atm/scspd/scsp_print.c
new file mode 100644
index 0000000..eb9d6f5
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_print.c
@@ -0,0 +1,1301 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Print routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Indent string
+ */
+#define MIN_INDENT 2
+#define MAX_INDENT 64
+static char indent[MAX_INDENT + 1];
+
+
+/*
+ * Value-name translation table entry
+ */
+struct type_name {
+ char *name;
+ u_char type;
+};
+typedef struct type_name Type_name;
+
+
+/*
+ * SCSP name-type tables
+ */
+static Type_name if_msg_types[] = {
+ { "Config Request", SCSP_CFG_REQ },
+ { "Config Response", SCSP_CFG_RSP },
+ { "Cache Indication", SCSP_CACHE_IND },
+ { "Cache Response", SCSP_CACHE_RSP },
+ { "Solicit Indication", SCSP_SOLICIT_IND },
+ { "Solicit Response", SCSP_SOLICIT_RSP },
+ { "Cache Update Indication", SCSP_UPDATE_IND },
+ { "Cache Update Request", SCSP_UPDATE_REQ },
+ { "Cache Update Response", SCSP_UPDATE_RSP },
+ { (char *)0, 0 }
+};
+
+static Type_name msg_types[] = {
+ { "Cache Alignment", SCSP_CA_MSG },
+ { "CSU Request", SCSP_CSU_REQ_MSG },
+ { "CSU Reply", SCSP_CSU_REPLY_MSG },
+ { "CSU Solicit", SCSP_CSUS_MSG },
+ { "Hello", SCSP_HELLO_MSG },
+ { (char *)0, 0 }
+};
+
+static Type_name proto_types[] = {
+ { "ATMARP", SCSP_PROTO_ATMARP },
+ { "NHRP", SCSP_PROTO_NHRP },
+ { "MARS", SCSP_PROTO_MARS },
+ { "DHCP", SCSP_PROTO_DHCP },
+ { "LNNI", SCSP_PROTO_LNNI },
+ { (char *)0, 0 }
+};
+
+static Type_name ext_types[] = {
+ { "End of Extensions", SCSP_EXT_END },
+ { "Authentication", SCSP_EXT_AUTH },
+ { "Vendor Private", SCSP_EXT_VENDOR },
+ { (char *)0, 0 }
+};
+
+static Type_name hfsm_state_names[] = {
+ { "Down", SCSP_HFSM_DOWN },
+ { "Waiting", SCSP_HFSM_WAITING },
+ { "Unidirectional", SCSP_HFSM_UNI_DIR },
+ { "Bidirectional", SCSP_HFSM_BI_DIR },
+ { (char *)0, 0 }
+};
+
+static Type_name hfsm_event_names[] = {
+ { "VC open", SCSP_HFSM_VC_ESTAB },
+ { "VC closed", SCSP_HFSM_VC_CLOSED },
+ { "Hello timer", SCSP_HFSM_HELLO_T },
+ { "Receive timer", SCSP_HFSM_RCV_T },
+ { "Msg received", SCSP_HFSM_RCVD },
+ { (char *)0, 0 }
+};
+
+static Type_name cafsm_state_names[] = {
+ { "Down", SCSP_CAFSM_DOWN },
+ { "Master/Slave negotiation", SCSP_CAFSM_NEG },
+ { "Master", SCSP_CAFSM_MASTER },
+ { "Slave", SCSP_CAFSM_SLAVE },
+ { "Update cache", SCSP_CAFSM_UPDATE },
+ { "Aligned", SCSP_CAFSM_ALIGNED },
+ { (char *)0, 0 }
+};
+
+static Type_name cafsm_event_names[] = {
+ { "Hello FSM up", SCSP_CAFSM_HELLO_UP },
+ { "Hello FSM down", SCSP_CAFSM_HELLO_DOWN },
+ { "CA received", SCSP_CAFSM_CA_MSG },
+ { "CSU Solicit received", SCSP_CAFSM_CSUS_MSG },
+ { "CSU Request received", SCSP_CAFSM_CSU_REQ },
+ { "CSU Reply received", SCSP_CAFSM_CSU_REPLY },
+ { "CA timer", SCSP_CAFSM_CA_T },
+ { "CSUS timer", SCSP_CAFSM_CSUS_T },
+ { "CSU timer", SCSP_CAFSM_CSU_T },
+ { "Cache Update", SCSP_CAFSM_CACHE_UPD },
+ { "Cache Response", SCSP_CAFSM_CACHE_RSP },
+ { (char *)0, 0 }
+};
+
+static Type_name cifsm_state_names[] = {
+ { "Null", SCSP_CIFSM_NULL },
+ { "Summarize", SCSP_CIFSM_SUM },
+ { "Update", SCSP_CIFSM_UPD },
+ { "Aligned", SCSP_CIFSM_ALIGN },
+ { (char *)0, 0 }
+};
+
+static Type_name cifsm_event_names[] = {
+ { "CA FSM down", SCSP_CIFSM_CA_DOWN },
+ { "CA FSM to Summarize",SCSP_CIFSM_CA_SUMM },
+ { "CA FSM to Update", SCSP_CIFSM_CA_UPD },
+ { "CA FSM to Aligned", SCSP_CIFSM_CA_ALIGN },
+ { "Solicit Rsp", SCSP_CIFSM_SOL_RSP },
+ { "Update Req", SCSP_CIFSM_UPD_REQ },
+ { "Update Rsp", SCSP_CIFSM_UPD_RSP },
+ { "CSU Request", SCSP_CIFSM_CSU_REQ },
+ { "CSU Reply", SCSP_CIFSM_CSU_REPLY },
+ { "CSU Solicit", SCSP_CIFSM_CSU_SOL },
+ { (char *)0, 0 }
+};
+
+static Type_name atmarp_state_names[] = {
+ { "New", SCSP_ASTATE_NEW },
+ { "Updated", SCSP_ASTATE_UPD },
+ { "Deleted", SCSP_ASTATE_DEL },
+ { (char *)0, 0 }
+};
+
+
+/*
+ * Initialize the indent string
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+init_indent()
+{
+ indent[0] = '\0';
+}
+
+
+/*
+ * Increment the indent string
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+inc_indent()
+{
+ if (strlen(indent) >= MAX_INDENT)
+ return;
+ strcat(indent, " ");
+}
+
+
+/*
+ * Decrement the indent string
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+dec_indent()
+{
+ if (strlen(indent) < MIN_INDENT)
+ return;
+ indent[strlen(indent) - 2] = '\0';
+}
+
+
+
+/*
+ * Search for a type in a name-type table
+ *
+ * Arguments:
+ * type the value being searched for
+ * tbl pointer to the table to search
+ *
+ * Returns:
+ * pointer to a string identifying the type
+ *
+ */
+static char *
+scsp_type_name(type, tbl)
+ u_char type;
+ Type_name *tbl;
+{
+ int i;
+
+ /*
+ * Search the table
+ */
+ for (i = 0; tbl[i].name != (char *)0 && tbl[i].type != type;
+ i++)
+ ;
+
+ /*
+ * Check the result and return the appropriate value
+ */
+ if (tbl[i].name)
+ return(tbl[i].name);
+ else
+ return("-");
+}
+
+
+/*
+ * Format a Hello FSM state name
+ *
+ * Arguments:
+ * state the state
+ *
+ * Returns:
+ * pointer to a string identifying the state
+ *
+ */
+char *
+format_hfsm_state(state)
+ int state;
+{
+ return(scsp_type_name((u_char)state, hfsm_state_names));
+}
+
+
+/*
+ * Format a Hello FSM event name
+ *
+ * Arguments:
+ * event the event
+ *
+ * Returns:
+ * pointer to a string identifying the event
+ *
+ */
+char *
+format_hfsm_event(event)
+ int event;
+{
+ char *cp;
+
+ cp = scsp_type_name((u_char)event, hfsm_event_names);
+ return(cp);
+}
+
+
+/*
+ * Format a CA FSM state name
+ *
+ * Arguments:
+ * state the state
+ *
+ * Returns:
+ * pointer to a string identifying the state
+ *
+ */
+char *
+format_cafsm_state(state)
+ int state;
+{
+ return(scsp_type_name((u_char)state, cafsm_state_names));
+}
+
+
+/*
+ * Format a CA FSM event name
+ *
+ * Arguments:
+ * event the event
+ *
+ * Returns:
+ * pointer to a string identifying the event
+ *
+ */
+char *
+format_cafsm_event(event)
+ int event;
+{
+ return(scsp_type_name((u_char)event, cafsm_event_names));
+}
+
+
+/*
+ * Format a client interface FSM state name
+ *
+ * Arguments:
+ * state the state
+ *
+ * Returns:
+ * pointer to a string identifying the state
+ *
+ */
+char *
+format_cifsm_state(state)
+ int state;
+{
+ return(scsp_type_name((u_char)state, cifsm_state_names));
+}
+
+
+/*
+ * Format a client interface FSM event name
+ *
+ * Arguments:
+ * event the event
+ *
+ * Returns:
+ * pointer to a string identifying the event
+ *
+ */
+char *
+format_cifsm_event(event)
+ int event;
+{
+ return(scsp_type_name((u_char)event, cifsm_event_names));
+}
+
+
+/*
+ * Print a Sender or Receiver ID structure
+ *
+ * Arguments:
+ * fp file to print message to
+ * idp pointer to ID to be printed
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_id(fp, idp)
+ FILE *fp;
+ Scsp_id *idp;
+{
+ int i;
+
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, idp->next);
+ fprintf(fp, "%sLength: %d\n", indent,
+ idp->id_len);
+ fprintf(fp, "%sID: 0x", indent);
+ for (i = 0; i < idp->id_len; i++)
+ fprintf(fp, "%02x ", idp->id[i]);
+ fprintf(fp, "\n");
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache Key structure
+ *
+ * Arguments:
+ * fp file to print message to
+ * ckp pointer to cache key structure
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_cache_key(fp, ckp)
+ FILE *fp;
+ Scsp_ckey *ckp;
+{
+ int i;
+
+ inc_indent();
+ fprintf(fp, "%sLength: %d\n", indent,
+ ckp->key_len);
+ fprintf(fp, "%sKey: 0x", indent);
+ for (i = 0; i < ckp->key_len; i++)
+ fprintf(fp, "%02x ", ckp->key[i]);
+ fprintf(fp, "\n");
+ dec_indent();
+}
+
+
+/*
+ * Print the mandatory common part of a message
+ *
+ * Arguments:
+ * fp file to print message to
+ * mcp pointer to mandatory common part structure
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_mcp(fp, mcp)
+ FILE *fp;
+ Scsp_mcp *mcp;
+{
+ inc_indent();
+ fprintf(fp, "%sProtocol ID: %s (0x%02x)\n", indent,
+ scsp_type_name(mcp->pid, proto_types),
+ mcp->pid);
+ fprintf(fp, "%sServer Group ID: %d\n", indent, mcp->sgid);
+ fprintf(fp, "%sFlags: 0x%04x\n", indent,
+ mcp->flags);
+ fprintf(fp, "%sRecord Count: %d\n", indent,
+ mcp->rec_cnt);
+ fprintf(fp, "%sSender ID:\n", indent);
+ print_scsp_id(fp, &mcp->sid);
+ fprintf(fp, "%sReceiver ID:\n", indent);
+ print_scsp_id(fp, &mcp->rid);
+ dec_indent();
+}
+
+
+/*
+ * Print an extension
+ *
+ * Arguments:
+ * fp file to print message to
+ * exp pointer to extension
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_ext(fp, exp)
+ FILE *fp;
+ Scsp_ext *exp;
+{
+ int i;
+ u_char *cp;
+
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, exp->next);
+ fprintf(fp, "%sType: %s (0x%02x)\n", indent,
+ scsp_type_name(exp->type, ext_types),
+ exp->type);
+ fprintf(fp, "%sLength: %d\n", indent, exp->len);
+ if (exp->len) {
+ fprintf(fp, "%sValue: 0x", indent);
+ cp = (u_char *)((caddr_t)exp + sizeof(Scsp_ext));
+ for (i = 0; i < exp->len; i++)
+ fprintf(fp, "%02x ", *cp++);
+ fprintf(fp, "\n");
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print an ATMARP Cache State Advertisement record
+ *
+ * Arguments:
+ * fp file to print message to
+ * acsp pointer to extension
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_atmarp_csa(fp, acsp)
+ FILE *fp;
+ Scsp_atmarp_csa *acsp;
+{
+ inc_indent();
+ fprintf(fp, "%sState: %s (%d)\n", indent,
+ scsp_type_name(acsp->sa_state,
+ atmarp_state_names),
+ acsp->sa_state);
+ fprintf(fp, "%sSource ATM addr: %s\n", indent,
+ format_atm_addr(&acsp->sa_sha));
+ fprintf(fp, "%sSource ATM subaddr: %s\n", indent,
+ format_atm_addr(&acsp->sa_ssa));
+ fprintf(fp, "%sSource IP addr: %s\n", indent,
+ format_ip_addr(&acsp->sa_spa));
+ fprintf(fp, "%sTarget ATM addr: %s\n", indent,
+ format_atm_addr(&acsp->sa_tha));
+ fprintf(fp, "%sTarget ATM subaddr: %s\n", indent,
+ format_atm_addr(&acsp->sa_tsa));
+ fprintf(fp, "%sTarget IP addr: %s\n", indent,
+ format_ip_addr(&acsp->sa_tpa));
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache State Advertisement record or
+ * Cache State Advertisement Summary record
+ *
+ * Arguments:
+ * fp file to print message to
+ * csap pointer to CSA or CSAS
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_csa(fp, csap)
+ FILE *fp;
+ Scsp_csa *csap;
+{
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, csap->next);
+ fprintf(fp, "%sHops: %d\n", indent, csap->hops);
+ fprintf(fp, "%sNull Flag: %s\n", indent,
+ csap->null ? "True" : "False");
+ fprintf(fp, "%sSequence no.: %ld (0x%lx)\n",
+ indent, csap->seq, csap->seq);
+ fprintf(fp, "%sCache Key:\n", indent);
+ print_scsp_cache_key(fp, &csap->key);
+ fprintf(fp, "%sOriginator ID:\n", indent);
+ print_scsp_id(fp, &csap->oid);
+ if (csap->atmarp_data) {
+ fprintf(fp, "%sATMARP data:\n", indent);
+ print_scsp_atmarp_csa(fp, csap->atmarp_data);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache Alignment message
+ *
+ * Arguments:
+ * fp file to print message to
+ * cap pointer to extension
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_ca(fp, cap)
+ FILE *fp;
+ Scsp_ca *cap;
+{
+ int n;
+ Scsp_csa *csap;
+
+ inc_indent();
+ fprintf(fp, "%sCA Seq. No.: %ld\n", indent,
+ cap->ca_seq);
+ fprintf(fp, "%sM bit: %s\n", indent,
+ cap->ca_m ? "True" : "False");
+ fprintf(fp, "%sI bit: %s\n", indent,
+ cap->ca_i ? "True" : "False");
+ fprintf(fp, "%sO bit: %s\n", indent,
+ cap->ca_o ? "True" : "False");
+ fprintf(fp, "%sMandatory Common Part:\n", indent);
+ print_scsp_mcp(fp, &cap->ca_mcp);
+ for (csap = cap->ca_csa_rec, n = 1; csap;
+ csap = csap->next, n++) {
+ fprintf(fp, "%sCSA Record %d (%p):\n", indent, n, csap);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print a Cache State Update Request, Cache State Update Reply, or
+ * Cache State Update Solicit message
+ *
+ * Arguments:
+ * fp file to print message to
+ * csup pointer to CSU message
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_csu(fp, csup)
+ FILE *fp;
+ Scsp_csu_msg *csup;
+{
+ int i;
+ Scsp_csa *csap;
+
+ inc_indent();
+ fprintf(fp, "%sMandatory Common Part:\n", indent);
+ print_scsp_mcp(fp, &csup->csu_mcp);
+ for (csap = csup->csu_csa_rec, i = 1; csap;
+ csap = csap->next, i++) {
+ fprintf(fp, "%sCSA Record %d:\n", indent, i);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print a Hello message
+ *
+ * Arguments:
+ * fp file to print message to
+ * hp pointer to hello message
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_hello(fp, hp)
+ FILE *fp;
+ Scsp_hello *hp;
+{
+ Scsp_id *ridp;
+
+ inc_indent();
+ fprintf(fp, "%sHello Interval: %d\n", indent,
+ hp->hello_int);
+ fprintf(fp, "%sDead Factor: %d\n", indent,
+ hp->dead_factor);
+ fprintf(fp, "%sFamily ID: %d\n", indent,
+ hp->family_id);
+ fprintf(fp, "%sMandatory Common Part:\n", indent);
+ print_scsp_mcp(fp, &hp->hello_mcp);
+ ridp = hp->hello_mcp.rid.next;
+ if (ridp) {
+ fprintf(fp, "%sAdditional Receiver IDs:\n", indent);
+ for (; ridp; ridp = ridp->next)
+ print_scsp_id(fp, ridp);
+ }
+ dec_indent();
+}
+
+
+#ifdef NOTDEF
+/*
+ * NHRP-specific Cache State Advertisement record
+ */
+struct scsp_nhrp_csa {
+ u_char req_id; /* Request ID */
+ u_char state; /* State */
+ u_char pref_len; /* Prefix length */
+ u_short flags; /* See below */
+ u_short mtu; /* Maximim transmission unit */
+ u_short hold_time; /* Entry holding time */
+ u_char caddr_tlen; /* Client addr type/length */
+ u_char csaddr_tlen; /* Client subaddr type/length */
+ u_char cproto_len; /* Client proto addr length */
+ u_char pref; /* Preference */
+ Atm_addr caddr; /* Client address */
+ Atm_addr csaddr; /* Client subaddress */
+ struct in_addr cproto_addr; /* Client protocol address */
+};
+typedef struct scsp_nhrp Scsp_nhrp;
+
+#define SCSP_NHRP_UNIQ 0x8000
+#define SCSP_NHRP_ARP 0x4000
+
+#endif
+
+
+/*
+ * Print an SCSP message
+ *
+ * Arguments:
+ * fp file to print message to
+ * msg pointer to message to be printed
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_msg(fp, msg)
+ FILE *fp;
+ Scsp_msg *msg;
+{
+ int n;
+ Scsp_ext *exp;
+
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print the message type
+ */
+ inc_indent();
+ fprintf(fp, "%sMessage type: %s (0x%02x)\n", indent,
+ scsp_type_name(msg->sc_msg_type, msg_types),
+ msg->sc_msg_type);
+
+ /*
+ * Print the body of the message
+ */
+ switch(msg->sc_msg_type) {
+ case SCSP_CA_MSG:
+ print_scsp_ca(fp, msg->sc_ca);
+ break;
+ case SCSP_CSU_REQ_MSG:
+ case SCSP_CSU_REPLY_MSG:
+ case SCSP_CSUS_MSG:
+ print_scsp_csu(fp, msg->sc_csu_msg);
+ break;
+ case SCSP_HELLO_MSG:
+ print_scsp_hello(fp, msg->sc_hello);
+ break;
+ }
+
+ /*
+ * Print any extensions
+ */
+ for (exp = msg->sc_ext, n = 1; exp; exp = exp->next, n++) {
+ fprintf(fp, "%sExtension %d:\n", indent, n);
+ print_scsp_ext(fp, exp);
+ }
+ dec_indent();
+
+ (void)fflush(fp);
+}
+
+
+/*
+ * Print an SCSP ATMARP message
+ *
+ * Arguments:
+ * fp file to print message to
+ * acp pointer to ATMARP message
+ *
+ * Returns:
+ * none
+ *
+ */
+static void
+print_scsp_if_atmarp(fp, amp)
+ FILE *fp;
+ Scsp_atmarp_msg *amp;
+{
+ inc_indent();
+ fprintf(fp, "%sState: %s (%d)\n", indent,
+ scsp_type_name(amp->sa_state,
+ atmarp_state_names),
+ amp->sa_state);
+ fprintf(fp, "%sCached protocol addr: %s\n", indent,
+ format_ip_addr(&amp->sa_cpa));
+ fprintf(fp, "%sCached ATM addr: %s\n", indent,
+ format_atm_addr(&amp->sa_cha));
+ fprintf(fp, "%sCached ATM subaddr: %s\n", indent,
+ format_atm_addr(&amp->sa_csa));
+ fprintf(fp, "%sCache key:\n", indent);
+ print_scsp_cache_key(fp, &amp->sa_key);
+ fprintf(fp, "%sOriginator ID:\n", indent);
+ print_scsp_id(fp, &amp->sa_oid);
+ fprintf(fp, "%sSequence number: %ld (0x%08lx)\n", indent,
+ amp->sa_seq, (u_long)amp->sa_seq);
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP client interface message
+ *
+ * Arguments:
+ * fp file to print message to
+ * imsg pointer to message to be printed
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_if_msg(fp, imsg)
+ FILE *fp;
+ Scsp_if_msg *imsg;
+{
+ int len;
+ Scsp_atmarp_msg *ap;
+
+ /*
+ * Initialize
+ */
+ init_indent();
+ fprintf(fp, "SCSP Client Interface Message at %p\n", imsg);
+
+ /*
+ * Print the message header
+ */
+ inc_indent();
+ fprintf(fp, "%sMessage type: %s (0x%02x)\n", indent,
+ scsp_type_name(imsg->si_type, if_msg_types),
+ imsg->si_type);
+ fprintf(fp, "%sResponse code: %d\n", indent,
+ imsg->si_rc);
+ fprintf(fp, "%sProtocol type: %s (%d)\n", indent,
+ scsp_type_name(imsg->si_proto, proto_types),
+ imsg->si_proto);
+ fprintf(fp, "%sLength: %d\n", indent,
+ imsg->si_len);
+ fprintf(fp, "%sToken: 0x%lx\n", indent,
+ imsg->si_tok);
+
+ /*
+ * Print the body of the message
+ */
+ switch(imsg->si_type) {
+ case SCSP_CFG_REQ:
+ fprintf(fp, "%sInterface: %s\n", indent,
+ imsg->si_cfg.atmarp_netif);
+ break;
+ case SCSP_CACHE_RSP:
+ case SCSP_UPDATE_IND:
+ case SCSP_UPDATE_REQ:
+ len = imsg->si_len - sizeof(Scsp_if_msg_hdr);
+ ap = &imsg->si_atmarp;
+ while (len) {
+ switch(imsg->si_proto) {
+ case SCSP_PROTO_ATMARP:
+ fprintf(fp, "%sATMARP CSA:\n", indent);
+ print_scsp_if_atmarp(fp, ap);
+ len -= sizeof(Scsp_atmarp_msg);
+ ap++;
+ break;
+ case SCSP_PROTO_NHRP:
+ case SCSP_PROTO_MARS:
+ case SCSP_PROTO_DHCP:
+ case SCSP_PROTO_LNNI:
+ default:
+ fprintf(fp, "Protocol type not implemented\n");
+ break;
+ }
+ }
+ break;
+ }
+ dec_indent();
+
+ (void)fflush(fp);
+}
+
+
+/*
+ * Print an SCSP pending connection block
+ *
+ * Arguments:
+ * fp file to print message to
+ * pp pointer to pending control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_pending(fp, pp)
+ FILE *fp;
+ Scsp_pending *pp;
+{
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print a header
+ */
+ fprintf(fp, "Pending control block at %p\n", pp);
+
+ /*
+ * Print the fields of the control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent, pp->sp_next);
+ fprintf(fp, "%sSocket: %d\n", indent,
+ pp->sp_sock);
+
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP server control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * ssp pointer to server control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_server(fp, ssp)
+ FILE *fp;
+ Scsp_server *ssp;
+{
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print a header
+ */
+ fprintf(fp, "Server control block at %p\n", ssp);
+
+ /*
+ * Print the fields of the client control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext: %p\n", indent,
+ ssp->ss_next);
+ fprintf(fp, "%sName: %s\n", indent,
+ ssp->ss_name);
+ fprintf(fp, "%sNetwork Interface: %s\n", indent,
+ ssp->ss_intf);
+ fprintf(fp, "%sState: %d\n", indent,
+ ssp->ss_state);
+ fprintf(fp, "%sProtocol ID: 0x%lx\n", indent,
+ ssp->ss_pid);
+ fprintf(fp, "%sID length: %d\n", indent,
+ ssp->ss_id_len);
+ fprintf(fp, "%sCache key length: %d\n", indent,
+ ssp->ss_ckey_len);
+ fprintf(fp, "%sServer Group ID: 0x%lx\n", indent,
+ ssp->ss_sgid);
+ fprintf(fp, "%sFamily ID: 0x%lx\n", indent,
+ ssp->ss_fid);
+ fprintf(fp, "%sSocket: %d\n", indent,
+ ssp->ss_sock);
+ fprintf(fp, "%sDCS Listen Socket: %d\n", indent,
+ ssp->ss_dcs_lsock);
+ fprintf(fp, "%sLocal Server ID:\n", indent);
+ print_scsp_id(fp, &ssp->ss_lsid);
+ fprintf(fp, "%sATM address: %s\n", indent,
+ format_atm_addr(&ssp->ss_addr));
+ fprintf(fp, "%sATM subaddress: %s\n", indent,
+ format_atm_addr(&ssp->ss_subaddr));
+ fprintf(fp, "%sInterface MTU: %d\n", indent,
+ ssp->ss_mtu);
+ fprintf(fp, "%sMark: %d\n", indent,
+ ssp->ss_mark);
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP client cache summary entry control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * csep pointer to summary entry
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_cse(fp, csep)
+ FILE *fp;
+ Scsp_cse *csep;
+{
+ /*
+ * Print the fields of the cache summary entry
+ */
+ inc_indent();
+ fprintf(fp, "%sNext CSE: %p\n", indent, csep->sc_next);
+ fprintf(fp, "%sCSA sequence no.: %ld (0x%lx)\n", indent,
+ csep->sc_seq, csep->sc_seq);
+ fprintf(fp, "%sCache key:\n", indent);
+ print_scsp_cache_key(fp, &csep->sc_key);
+ fprintf(fp, "%sOrigin ID:\n", indent);
+ print_scsp_id(fp, &csep->sc_oid);
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP CSU Request retransmission control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * csurp pointer to retransmission entry
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_csu_rexmt(fp, rxp)
+ FILE *fp;
+ Scsp_csu_rexmt *rxp;
+{
+ int i;
+ Scsp_csa *csap;
+
+ inc_indent();
+ fprintf(fp, "%sNext CSU Req rexmt: %p\n", indent, rxp->sr_next);
+ fprintf(fp, "%sDCS address: %p\n", indent, rxp->sr_dcs);
+ for (csap = rxp->sr_csa, i = 1; csap;
+ csap = csap->next, i++) {
+ fprintf(fp, "%sCSA %d:\n", indent, i);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+}
+
+
+/*
+ * Print an SCSP DCS control block
+ *
+ * Arguments:
+ * fp file to print message to
+ * dcsp pointer to DCS control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+print_scsp_dcs(fp, dcsp)
+ FILE *fp;
+ Scsp_dcs *dcsp;
+{
+ Scsp_csa *csap;
+ Scsp_cse *csep;
+ Scsp_csu_rexmt *rxp;
+
+ /*
+ * Initialize
+ */
+ init_indent();
+
+ /*
+ * Print a header
+ */
+ fprintf(fp, "DCS control block at %p\n", dcsp);
+
+ /*
+ * Print the fields of the DCS control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext DCS block: %p\n", indent, dcsp->sd_next);
+ fprintf(fp, "%sServer control block: %p\n", indent, dcsp->sd_server);
+ fprintf(fp, "%sDCS ID:\n", indent);
+ print_scsp_id(fp, &dcsp->sd_dcsid);
+ fprintf(fp, "%sDCS address: %s\n", indent,
+ format_atm_addr(&dcsp->sd_addr));
+ fprintf(fp, "%sDCS subaddress %s\n", indent,
+ format_atm_addr(&dcsp->sd_subaddr));
+ fprintf(fp, "%sSocket: %d\n", indent,
+ dcsp->sd_sock);
+ fprintf(fp, "%sOpen VCC Retry Timer:\n", indent);
+ fprintf(fp, "%sHello FSM State: %s\n", indent,
+ format_hfsm_state(dcsp->sd_hello_state));
+ fprintf(fp, "%sHello Interval: %d\n", indent,
+ dcsp->sd_hello_int);
+ fprintf(fp, "%sHello Dead Factor: %d\n", indent,
+ dcsp->sd_hello_df);
+ fprintf(fp, "%sHello Rcvd: %d\n", indent,
+ dcsp->sd_hello_rcvd);
+ fprintf(fp, "%sCA FSM State: %s\n", indent,
+ format_cafsm_state(dcsp->sd_ca_state));
+ fprintf(fp, "%sCA Seq. No.: 0x%lx\n", indent,
+ dcsp->sd_ca_seq);
+ fprintf(fp, "%sCA Rexmit Int: %d\n", indent,
+ dcsp->sd_ca_rexmt_int);
+ fprintf(fp, "%sCA Retransmit Msg: %p\n", indent,
+ dcsp->sd_ca_rexmt_msg);
+ fprintf(fp, "%sCSASs to send: ", indent);
+ if (dcsp->sd_ca_csas == (Scsp_cse *)0) {
+ fprintf(fp, "Empty\n");
+ } else {
+ fprintf(fp, "%p\n", dcsp->sd_ca_csas);
+ }
+ fprintf(fp, "%sCSUS Rexmit Int: %d\n", indent,
+ dcsp->sd_csus_rexmt_int);
+ fprintf(fp, "%sCache Request List: ", indent);
+ if (dcsp->sd_crl == (Scsp_csa *)0) {
+ fprintf(fp, "Empty\n");
+ } else {
+ fprintf(fp, "%p\n", dcsp->sd_crl);
+ }
+ fprintf(fp, "%sCSUS Rexmit Msg: %p\n", indent,
+ dcsp->sd_csus_rexmt_msg);
+ fprintf(fp, "%sCSA Hop count: %d\n", indent,
+ dcsp->sd_hops);
+ fprintf(fp, "%sCSAs Pending ACK: %p\n", indent,
+ dcsp->sd_csu_ack_pend);
+ fprintf(fp, "%sCSAs ACKed: %p\n", indent,
+ dcsp->sd_csu_ack);
+ fprintf(fp, "%sCSU Req Rexmit Int: %d\n", indent,
+ dcsp->sd_csu_rexmt_int);
+ fprintf(fp, "%sCSU Req Rexmit Max: %d\n", indent,
+ dcsp->sd_csu_rexmt_max);
+ fprintf(fp, "%sCSU Req Rexmit Queue ", indent);
+ if (!dcsp->sd_csu_rexmt) {
+ fprintf(fp, "Empty\n");
+ } else {
+ fprintf(fp, "%p\n", dcsp->sd_csu_rexmt);
+ }
+ fprintf(fp, "%sClient I/F state: %d\n", indent,
+ dcsp->sd_client_state);
+
+ /*
+ * Print the list of CSASs waiting to be sent
+ */
+ if (dcsp->sd_ca_csas) {
+ fprintf(fp, "\n%sCSASs to send:", indent);
+ inc_indent();
+ for (csep = dcsp->sd_ca_csas; csep;
+ csep = csep->sc_next) {
+ fprintf(fp, "%sCache summary entry at %p\n",
+ indent, csep);
+ print_scsp_cse(fp, csep);
+ }
+ dec_indent();
+ }
+
+ /*
+ * Print the Cache Request List
+ */
+ if (dcsp->sd_crl) {
+ fprintf(fp, "\n%sCache Request List:\n", indent);
+ inc_indent();
+ for (csap = dcsp->sd_crl; csap; csap = csap->next) {
+ fprintf(fp, "%sCSA at %p\n", indent, csap);
+ print_scsp_csa(fp, csap);
+ }
+ dec_indent();
+ }
+
+ /*
+ * Print the CSU retransmit queue
+ */
+ if (dcsp->sd_csu_rexmt) {
+ fprintf(fp, "\n%sCSU Req Rexmit Queue:\n", indent);
+ inc_indent();
+ for (rxp = dcsp->sd_csu_rexmt; rxp;
+ rxp = rxp->sr_next) {
+ fprintf(fp, "%sCSU Rexmit Block at %p\n",
+ indent, rxp);
+ print_scsp_csu_rexmt(fp, rxp);
+ }
+ dec_indent();
+ }
+
+ dec_indent();
+}
+
+
+/*
+ * Print SCSP's control blocks
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+print_scsp_dump()
+{
+ int i;
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp;
+ Scsp_cse *scp;
+ Scsp_pending *pp;
+ FILE *df;
+ char fname[64];
+ static int dump_no = 0;
+
+ /*
+ * Build a file name
+ */
+ bzero(fname, sizeof(fname));
+ sprintf(fname, "/tmp/scspd.%d.%03d.out", getpid(), dump_no++);
+
+ /*
+ * Open the output file
+ */
+ df = fopen(fname, "w");
+ if (df == (FILE *)0)
+ return;
+
+ /*
+ * Dump the server control blocks
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ print_scsp_server(df, ssp);
+ fprintf(df, "\n");
+
+ /*
+ * Print the client's cache summary
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (scp = ssp->ss_cache[i]; scp;
+ scp = scp->sc_next) {
+ print_scsp_cse(df, scp);
+ fprintf(df, "\n");
+ }
+ }
+
+ /*
+ * Print the client's DCS control blocks
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ print_scsp_dcs(df, dcsp);
+ fprintf(df, "\n\n");
+ }
+ fprintf(df, "\n\n");
+ }
+
+ /*
+ * Print the pending connection blocks
+ */
+ for (pp = scsp_pending_head; pp; pp = pp->sp_next) {
+ print_scsp_pending(df, pp);
+ fprintf(df, "\n");
+ }
+
+ /*
+ * Close the output file
+ */
+ (void)fclose(df);
+}
diff --git a/usr.sbin/atm/scspd/scsp_socket.c b/usr.sbin/atm/scspd/scsp_socket.c
new file mode 100644
index 0000000..3cfa294
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_socket.c
@@ -0,0 +1,1344 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP socket management routines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Local variables
+ */
+static struct t_atm_llc llc_scsp = {
+ T_ATM_LLC_SHARING,
+ 8,
+ {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x5e, 0x00, 0x05}
+};
+
+static struct t_atm_aal5 aal5 = {
+ 0, /* forward_max_SDU_size */
+ 0, /* backward_max_SDU_size */
+ 0 /* SSCS_type */
+};
+
+static struct t_atm_traffic traffic = {
+ { /* forward */
+ T_ATM_ABSENT, /* PCR_high_priority */
+ 0, /* PCR_all_traffic */
+ T_ATM_ABSENT, /* SCR_high_priority */
+ T_ATM_ABSENT, /* SCR_all_traffic */
+ T_ATM_ABSENT, /* MBS_high_priority */
+ T_ATM_ABSENT, /* MBS_all_traffic */
+ T_NO /* tagging */
+ },
+ { /* backward */
+ T_ATM_ABSENT, /* PCR_high_priority */
+ 0, /* PCR_all_traffic */
+ T_ATM_ABSENT, /* SCR_high_priority */
+ T_ATM_ABSENT, /* SCR_all_traffic */
+ T_ATM_ABSENT, /* MBS_high_priority */
+ T_ATM_ABSENT, /* MBS_all_traffic */
+ T_NO /* tagging */
+ },
+ T_YES /* best_effort */
+};
+
+static struct t_atm_bearer bearer = {
+ T_ATM_CLASS_X, /* bearer_class */
+ T_ATM_NULL, /* traffic_type */
+ T_ATM_NULL, /* timing_requirements */
+ T_NO, /* clipping_susceptibility */
+ T_ATM_1_TO_1 /* connection_configuration */
+};
+
+static struct t_atm_qos qos = {
+ T_ATM_NETWORK_CODING, /* coding_standard */
+ { /* forward */
+ T_ATM_QOS_CLASS_0 /* qos_class */
+ },
+ { /* backward */
+ T_ATM_QOS_CLASS_0 /* qos_class */
+ }
+};
+
+static struct t_atm_app_name appname = {
+ "SCSP"
+};
+
+
+/*
+ * Find a DCS, given its socket
+ *
+ * Arguments:
+ * sd socket descriptor
+ *
+ * Returns:
+ * 0 not found
+ * address of DCS block corresponding to socket
+ *
+ */
+Scsp_dcs *
+scsp_find_dcs(sd)
+ int sd;
+{
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp = NULL;
+
+ /*
+ * Loop through the list of servers
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ /*
+ * Check all the DCSs chained from each server
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ if (dcsp->sd_sock == sd)
+ break;
+ }
+ }
+
+ return(dcsp);
+}
+
+
+/*
+ * Find a server, given its socket
+ *
+ * Arguments:
+ * sd socket descriptor
+ *
+ * Returns:
+ * 0 not found
+ * address of server block corresponding to socket
+ *
+ */
+Scsp_server *
+scsp_find_server(sd)
+ int sd;
+{
+ Scsp_server *ssp;
+
+ /*
+ * Loop through the list of servers
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_sock == sd)
+ break;
+ }
+
+ return(ssp);
+}
+
+
+/*
+ * Connect to a directly connected server
+ *
+ * Arguments:
+ * dcsp pointer to DCS block for server
+ *
+ * Returns:
+ * 0 success (dcsp->sd_sock is set)
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_dcs_connect(dcsp)
+ Scsp_dcs *dcsp;
+
+{
+ int rc, sd;
+ struct sockaddr_atm DCS_addr;
+
+ /*
+ * If the DCS already has an open connection, just return
+ */
+ if (dcsp->sd_sock != -1) {
+ return(0);
+ }
+
+ /*
+ * Open an ATM socket
+ */
+ sd = socket(PF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5);
+ if (sd == -1) {
+ return(ESOCKTNOSUPPORT);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Set up connection parameters for SCSP connection
+ */
+ bzero(&DCS_addr, sizeof(DCS_addr));
+#if (defined(BSD) && (BSD >= 199103))
+ DCS_addr.satm_len = sizeof(DCS_addr);
+#endif
+ DCS_addr.satm_family = AF_ATM;
+ DCS_addr.satm_addr.t_atm_sap_addr.SVE_tag_addr =
+ T_ATM_PRESENT;
+ DCS_addr.satm_addr.t_atm_sap_addr.SVE_tag_selector =
+ T_ATM_PRESENT;
+ DCS_addr.satm_addr.t_atm_sap_addr.address_format =
+ dcsp->sd_addr.address_format;
+ DCS_addr.satm_addr.t_atm_sap_addr.address_length =
+ dcsp->sd_addr.address_length;
+ bcopy(dcsp->sd_addr.address,
+ DCS_addr.satm_addr.t_atm_sap_addr.address,
+ dcsp->sd_addr.address_length);
+
+ DCS_addr.satm_addr.t_atm_sap_layer2.SVE_tag =
+ T_ATM_PRESENT;
+ DCS_addr.satm_addr.t_atm_sap_layer2.ID_type =
+ T_ATM_SIMPLE_ID;
+ DCS_addr.satm_addr.t_atm_sap_layer2.ID.simple_ID =
+ T_ATM_BLLI2_I8802;
+
+ DCS_addr.satm_addr.t_atm_sap_layer3.SVE_tag =
+ T_ATM_ABSENT;
+ DCS_addr.satm_addr.t_atm_sap_appl.SVE_tag =
+ T_ATM_ABSENT;
+
+ /*
+ * Bind the socket to our address
+ */
+ if (bind(sd, (struct sockaddr *)&DCS_addr,
+ sizeof(DCS_addr))) {
+ rc = errno;
+ goto connect_fail;
+ }
+
+ /*
+ * Set non-blocking operation
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ scsp_log(LOG_ERR, "scsp_dcs_connect: fcntl failed");
+ rc = errno;
+ goto connect_fail;
+ }
+
+ /*
+ * Set AAL 5 options
+ */
+ aal5.forward_max_SDU_size = dcsp->sd_server->ss_mtu;
+ aal5.backward_max_SDU_size = dcsp->sd_server->ss_mtu;
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_AAL5, (caddr_t)&aal5,
+ sizeof(aal5)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set traffic options
+ */
+ switch(dcsp->sd_server->ss_media) {
+ case MEDIA_TAXI_100:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI100;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI100;
+ break;
+ case MEDIA_TAXI_140:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI140;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI140;
+ break;
+ case MEDIA_OC3C:
+ case MEDIA_UTP155:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC3C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC3C;
+ break;
+ case MEDIA_OC12C:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC12C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC12C;
+ break;
+ case MEDIA_UNKNOWN:
+ break;
+ }
+
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_TRAFFIC,
+ (caddr_t)&traffic, sizeof(traffic)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set bearer capability options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_BEARER_CAP,
+ (caddr_t)&bearer, sizeof(bearer)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set QOS options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_QOS,
+ (caddr_t)&qos, sizeof(qos)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set LLC identifier
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_LLC,
+ (caddr_t)&llc_scsp, sizeof(llc_scsp)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Set application name
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
+ (caddr_t)&appname, sizeof(appname)) < 0) {
+ rc = EOPNOTSUPP;
+ goto connect_fail;
+ }
+
+ /*
+ * Connect to DCS
+ */
+ if (connect(sd, (struct sockaddr *)&DCS_addr,
+ sizeof(DCS_addr)) < 0 &&
+ errno != EINPROGRESS) {
+ rc = errno;
+ goto connect_fail;
+ }
+
+ /*
+ * Set return values
+ */
+ dcsp->sd_sock = sd;
+ return(0);
+
+connect_fail:
+ /*
+ * Close the socket if something didn't work
+ */
+ (void)close(sd);
+ dcsp->sd_sock = -1;
+ if (rc == 0)
+ rc = EFAULT;
+ return(rc);
+}
+
+
+/*
+ * Listen for ATM connections from DCSs
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * sock socket which is listening (also set in
+ ssp->ss_dcs_lsock)
+ * -1 error encountered (reason in errno)
+ *
+ */
+int
+scsp_dcs_listen(ssp)
+ Scsp_server *ssp;
+{
+ int rc, sd;
+ struct sockaddr_atm ls_addr;
+
+ /*
+ * Open a socket
+ */
+ sd = socket(PF_ATM, SOCK_SEQPACKET, ATM_PROTO_AAL5);
+ if (sd == -1) {
+ rc = errno;
+ goto listen_fail;
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Set up our address
+ */
+ bzero(&ls_addr, sizeof(ls_addr));
+#if (defined(BSD) && (BSD >= 199103))
+ ls_addr.satm_len = sizeof(ls_addr);
+#endif
+ ls_addr.satm_family = AF_ATM;
+ ls_addr.satm_addr.t_atm_sap_addr.SVE_tag_addr = T_ATM_PRESENT;
+ ls_addr.satm_addr.t_atm_sap_addr.SVE_tag_selector =
+ T_ATM_PRESENT;
+ ls_addr.satm_addr.t_atm_sap_addr.address_format =
+ ssp->ss_addr.address_format;
+ ls_addr.satm_addr.t_atm_sap_addr.address_length =
+ ssp->ss_addr.address_length;
+ bcopy(ssp->ss_addr.address,
+ ls_addr.satm_addr.t_atm_sap_addr.address,
+ ssp->ss_addr.address_length);
+
+ ls_addr.satm_addr.t_atm_sap_layer2.SVE_tag = T_ATM_PRESENT;
+ ls_addr.satm_addr.t_atm_sap_layer2.ID_type = T_ATM_SIMPLE_ID;
+ ls_addr.satm_addr.t_atm_sap_layer2.ID.simple_ID =
+ T_ATM_BLLI2_I8802;
+
+ ls_addr.satm_addr.t_atm_sap_layer3.SVE_tag = T_ATM_ABSENT;
+ ls_addr.satm_addr.t_atm_sap_appl.SVE_tag = T_ATM_ABSENT;
+
+ /*
+ * Bind the socket to our address
+ */
+ rc = bind(sd, (struct sockaddr *)&ls_addr, sizeof(ls_addr));
+ if (rc == -1) {
+ rc = errno;
+ goto listen_fail;
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ scsp_log(LOG_ERR, "scsp_dcs_listen: fcntl failed");
+ rc = errno;
+ goto listen_fail;
+ }
+
+ /*
+ * Set AAL 5 options
+ */
+ aal5.forward_max_SDU_size = ssp->ss_mtu;
+ aal5.backward_max_SDU_size = ssp->ss_mtu;
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_AAL5, (caddr_t)&aal5,
+ sizeof(aal5)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set traffic options
+ */
+ switch(ssp->ss_media) {
+ case MEDIA_TAXI_100:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI100;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI100;
+ break;
+ case MEDIA_TAXI_140:
+ traffic.forward.PCR_all_traffic = ATM_PCR_TAXI140;
+ traffic.backward.PCR_all_traffic = ATM_PCR_TAXI140;
+ break;
+ case MEDIA_OC3C:
+ case MEDIA_UTP155:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC3C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC3C;
+ break;
+ case MEDIA_OC12C:
+ traffic.forward.PCR_all_traffic = ATM_PCR_OC12C;
+ traffic.backward.PCR_all_traffic = ATM_PCR_OC12C;
+ break;
+ case MEDIA_UNKNOWN:
+ break;
+ }
+
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_TRAFFIC,
+ (caddr_t)&traffic, sizeof(traffic)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set bearer capability options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_BEARER_CAP,
+ (caddr_t)&bearer, sizeof(bearer)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set QOS options
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_QOS,
+ (caddr_t)&qos, sizeof(qos)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set LLC identifier
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_LLC,
+ (caddr_t)&llc_scsp, sizeof(llc_scsp)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Set application name
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
+ (caddr_t)&appname, sizeof(appname)) < 0) {
+ rc = EOPNOTSUPP;
+ goto listen_fail;
+ }
+
+ /*
+ * Listen for new connections
+ */
+ if (listen(sd, 5) < 0) {
+ rc = errno;
+ goto listen_fail;
+ }
+
+ ssp->ss_dcs_lsock = sd;
+ return(sd);
+
+listen_fail:
+ /*
+ * Close the socket if anything didn't work
+ */
+ (void)close(sd);
+ if (rc == 0)
+ errno = EFAULT;
+ else
+ errno = rc;
+ ssp->ss_dcs_lsock = -1;
+ return(-1);
+}
+
+
+/*
+ * Accept a connection from a DCS
+ *
+ * Arguments:
+ * ssp pointer to server block
+ *
+ * Returns:
+ * address of DCS with new connection
+ * 0 failure (errno has reason)
+ *
+ */
+Scsp_dcs *
+scsp_dcs_accept(ssp)
+ Scsp_server *ssp;
+{
+ int len, rc, sd;
+ struct sockaddr_atm dcs_sockaddr;
+ struct t_atm_sap_addr *dcs_addr = &dcs_sockaddr.satm_addr.t_atm_sap_addr;
+ Atm_addr dcs_atmaddr;
+ Scsp_dcs *dcsp;
+
+ /*
+ * Accept the new connection
+ */
+ len = sizeof(dcs_sockaddr);
+ sd = accept(ssp->ss_dcs_lsock,
+ (struct sockaddr *)&dcs_sockaddr, &len);
+ if (sd < 0) {
+ return((Scsp_dcs *)0);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Copy the DCS's address from the sockaddr to an Atm_addr
+ */
+ if (dcs_addr->SVE_tag_addr != T_ATM_PRESENT) {
+ dcs_atmaddr.address_format = T_ATM_ABSENT;
+ dcs_atmaddr.address_length = 0;
+ } else {
+ dcs_atmaddr.address_format = dcs_addr->address_format;
+ dcs_atmaddr.address_length = dcs_addr->address_length;
+ bcopy(dcs_addr->address, dcs_atmaddr.address,
+ dcs_addr->address_length);
+ }
+
+ /*
+ * Find out which DCS this connection is for
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ /*
+ * Compare DCS's address to address
+ * configured by user
+ */
+ if (ATM_ADDR_EQUAL(&dcsp->sd_addr,
+ &dcs_atmaddr))
+ break;
+ }
+
+ /*
+ * Make sure we have this DCS configured
+ */
+ if (!dcsp) {
+ errno = EINVAL;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Make sure we are in a state to accept the connection
+ */
+ if (ssp->ss_state != SCSP_SS_ACTIVE) {
+ errno = EACCES;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Make sure we don't already have a connection to this DCS
+ */
+ if (dcsp->sd_sock != -1) {
+ errno = EALREADY;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Set application name
+ */
+ if (setsockopt(sd, T_ATM_SIGNALING, T_ATM_APP_NAME,
+ (caddr_t)&appname, sizeof(appname)) < 0) {
+ rc = EOPNOTSUPP;
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ goto dcs_accept_fail;
+ }
+
+ /*
+ * Cancel the open retry timer
+ */
+ HARP_CANCEL(&dcsp->sd_open_t);
+
+ /*
+ * Save the socket address and return the
+ * address of the DCS
+ */
+ dcsp->sd_sock = sd;
+ return(dcsp);
+
+dcs_accept_fail:
+ /*
+ * An error has occured--clean up and return
+ */
+ (void)close(sd);
+ return((Scsp_dcs *)0);
+}
+
+
+/*
+ * Read an SCSP message from a directly connected server
+ *
+ * Arguments:
+ * dcsp pointer to DCS block that has data
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_dcs_read(dcsp)
+ Scsp_dcs *dcsp;
+
+{
+ int len, rc;
+ char *buff = (char *)0;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_msg *msg;
+
+ /*
+ * Get a buffer to hold the entire message
+ */
+ len = ssp->ss_mtu;
+ buff = calloc(1, len);
+ if (buff == NULL)
+ scsp_mem_err("scsp_dcs_read: ssp->ss_mtu");
+
+ /*
+ * Read the message
+ */
+ len = read(dcsp->sd_sock, buff, len);
+ if (len < 0) {
+ goto dcs_read_fail;
+ }
+
+ /*
+ * Parse the input message and pass it to the Hello FSM
+ */
+ msg = scsp_parse_msg(buff, len);
+ if (msg) {
+ /*
+ * Write the message to the trace file if
+ * it's of a type we're tracing
+ */
+ if (((scsp_trace_mode & SCSP_TRACE_HELLO_MSG) &&
+ msg->sc_msg_type == SCSP_HELLO_MSG) ||
+ ((scsp_trace_mode & SCSP_TRACE_CA_MSG) &&
+ msg->sc_msg_type != SCSP_HELLO_MSG)) {
+ scsp_trace_msg(dcsp, msg, 1);
+ scsp_trace("\n");
+ }
+
+ /*
+ * Pass the message to the Hello FSM
+ */
+ rc = scsp_hfsm(dcsp, SCSP_HFSM_RCVD, msg);
+ scsp_free_msg(msg);
+ } else {
+ /*
+ * Message was invalid. Write it to the trace file
+ * if we're tracing messages.
+ */
+ if (scsp_trace_mode & (SCSP_TRACE_HELLO_MSG &
+ SCSP_TRACE_CA_MSG)) {
+ int i;
+ scsp_trace("Invalid message received:\n");
+ scsp_trace("0x");
+ for (i = 0; i < len; i++) {
+ scsp_trace("%02x ", (u_char)buff[i]);
+ }
+ scsp_trace("\n");
+ }
+ }
+ free(buff);
+
+ return(0);
+
+dcs_read_fail:
+ /*
+ * Error on read--check for special conditions
+ */
+ rc = errno;
+ if (errno == ECONNRESET) {
+ /*
+ * VCC has been closed--pass the event to
+ * the Hello FSM
+ */
+ rc = scsp_hfsm(dcsp, SCSP_HFSM_VC_CLOSED,
+ (Scsp_msg *)0);
+ }
+ if (errno == ECONNREFUSED) {
+ /*
+ * VCC open failed--set a timer and try
+ * again when it fires
+ */
+ HARP_TIMER(&dcsp->sd_open_t,
+ SCSP_Open_Interval,
+ scsp_open_timeout);
+ rc = 0;
+ }
+
+ if (buff)
+ free(buff);
+ return(rc);
+}
+
+
+/*
+ * Listen for Unix connections from SCSP client servers
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * sock socket which is listening
+ * -1 error (reason in errno)
+ *
+ */
+int
+scsp_server_listen()
+{
+ int rc, sd;
+
+ static struct sockaddr scsp_addr = {
+#if (defined(BSD) && (BSD >= 199103))
+ sizeof(struct sockaddr), /* sa_len */
+#endif
+ AF_UNIX, /* sa_family */
+ SCSPD_SOCK_NAME /* sa_data */
+ };
+
+ /*
+ * Unlink any old socket
+ */
+ rc = unlink(SCSPD_SOCK_NAME);
+ if (rc < 0 && errno != ENOENT)
+ return(-1);
+
+ /*
+ * Open a socket
+ */
+ sd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sd == -1) {
+ return(-1);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Bind the socket's address
+ */
+ rc = bind(sd, &scsp_addr, sizeof(scsp_addr));
+ if (rc == -1) {
+ (void)close(sd);
+ return(-1);
+ }
+
+ /*
+ * Set non-blocking I/O
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ (void)close(sd);
+ return(-1);
+ }
+
+ /*
+ * Listen for new connections
+ */
+ if (listen(sd, 5) < 0) {
+ (void)close(sd);
+ return(-1);
+ }
+
+ return(sd);
+}
+
+
+/*
+ * Accept a connection from a server
+ *
+ * We accept a connection, but we won't know which server it is
+ * from until we get the configuration data from the server. We
+ * put the connection on a 'pending' queue and will assign it to
+ * a server when the config data arrives.
+ *
+ * Arguments:
+ * ls listening socket to accept from
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+scsp_server_accept(ls)
+ int ls;
+
+{
+ int len, rc, sd;
+ struct sockaddr server_addr;
+ Scsp_pending *psp;
+
+ /*
+ * Accept the new connection
+ */
+ len = sizeof(server_addr);
+ sd = accept(ls, (struct sockaddr *)&server_addr, &len);
+ if (sd < 0) {
+ return(errno);
+ }
+ if (sd > scsp_max_socket) {
+ scsp_max_socket = sd;
+ }
+
+ /*
+ * Set non-blocking operation
+ */
+#ifdef sun
+ rc = fcntl(sd, F_SETFL, FNBIO + FNDELAY);
+#else
+ rc = fcntl(sd, F_SETFL, O_NONBLOCK);
+#endif
+ if (rc == -1) {
+ (void)close(sd);
+ rc = errno;
+ }
+
+ /*
+ * Put the new socket on the 'pending' queue
+ */
+ psp = calloc(1, sizeof(Scsp_pending));
+ if (!psp)
+ scsp_mem_err("scsp_server_accept: sizeof(Scsp_pending)");
+ psp->sp_sock = sd;
+ LINK2TAIL(psp, Scsp_pending, scsp_pending_head, sp_next);
+
+ return(0);
+}
+
+
+/*
+ * Read a server interface message from a socket
+ *
+ * Arguments:
+ * sd socket to read from
+ *
+ * Returns:
+ * msg pointer to message read
+ * 0 failure (errno has reason)
+ *
+ */
+Scsp_if_msg *
+scsp_if_sock_read(sd)
+ int sd;
+
+{
+ int len;
+ char *buff = (char *)0;
+ Scsp_if_msg *msg;
+ Scsp_if_msg_hdr msg_hdr;
+
+ /*
+ * Read the message header from the socket
+ */
+ len = read(sd, (char *)&msg_hdr, sizeof(msg_hdr));
+ if (len != sizeof(msg_hdr)) {
+ if (len >= 0)
+ errno = EINVAL;
+ goto socket_read_fail;
+ }
+
+ /*
+ * Get a buffer and read the rest of the message into it
+ */
+ buff = malloc(msg_hdr.sh_len);
+ if (buff == NULL)
+ scsp_mem_err("scsp_if_sock_read: msg_hdr.sh_len");
+ msg = (Scsp_if_msg *)buff;
+ msg->si_hdr = msg_hdr;
+ len = read(sd, &buff[sizeof(Scsp_if_msg_hdr)],
+ msg->si_len - sizeof(Scsp_if_msg_hdr));
+ if (len != msg->si_len - sizeof(Scsp_if_msg_hdr)) {
+ if (len >= 0) {
+ errno = EINVAL;
+ }
+ goto socket_read_fail;
+ }
+
+ /*
+ * Trace the message
+ */
+ if (scsp_trace_mode & SCSP_TRACE_IF_MSG) {
+ scsp_trace("Received server I/F message:\n");
+ print_scsp_if_msg(scsp_trace_file, msg);
+ scsp_trace("\n");
+ }
+
+ return(msg);
+
+socket_read_fail:
+ if (buff)
+ free(buff);
+ return((Scsp_if_msg *)0);
+}
+
+
+/*
+ * Write a server interface message to a socket
+ *
+ * Arguments:
+ * sd socket to write to
+ * msg pointer to message to write
+ *
+ * Returns:
+ * 0 success
+ * errno reason for failure
+ *
+ */
+int
+scsp_if_sock_write(sd, msg)
+ int sd;
+ Scsp_if_msg *msg;
+{
+ int len, rc;
+
+ /*
+ * Trace the message
+ */
+ if (scsp_trace_mode & SCSP_TRACE_IF_MSG) {
+ scsp_trace("Writing server I/F message:\n");
+ print_scsp_if_msg(scsp_trace_file, msg);
+ scsp_trace("\n");
+ }
+
+ /*
+ * Write the message to the indicated socket
+ */
+ len = write(sd, (char *)msg, msg->si_len);
+ if (len != msg->si_len) {
+ if (len < 0)
+ rc = errno;
+ else
+ rc = EINVAL;
+ } else {
+ rc = 0;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Read data from a local server
+ *
+ * Arguments:
+ * ssp pointer to server block that has data
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_server_read(ssp)
+ Scsp_server *ssp;
+{
+ int rc;
+ Scsp_dcs *dcsp;
+ Scsp_if_msg *msg;
+
+ /*
+ * Read the message
+ */
+ msg = scsp_if_sock_read(ssp->ss_sock);
+ if (!msg) {
+ if (errno == EWOULDBLOCK) {
+ /*
+ * Nothing to read--just return
+ */
+ return(0);
+ } else {
+ /*
+ * Error--shut down the server entry
+ */
+ scsp_server_shutdown(ssp);
+ }
+ return(errno);
+ }
+
+ /*
+ * Process the received message
+ */
+ switch(msg->si_type) {
+ case SCSP_NOP_REQ:
+ /*
+ * Ignore a NOP
+ */
+ break;
+ case SCSP_CACHE_RSP:
+ /*
+ * Summarize the server's cache and try to open
+ * connections to all of its DCSs
+ */
+ scsp_process_cache_rsp(ssp, msg);
+ ssp->ss_state = SCSP_SS_ACTIVE;
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ if (scsp_dcs_connect(dcsp)) {
+ /*
+ * Connect failed -- the DCS may not
+ * be up yet, so we'll try again later
+ */
+ HARP_TIMER(&dcsp->sd_open_t,
+ SCSP_Open_Interval,
+ scsp_open_timeout);
+ }
+ }
+ ssp->ss_state = SCSP_SS_ACTIVE;
+ break;
+ case SCSP_SOLICIT_RSP:
+ /*
+ * The server has answered our request for a particular
+ * entry from its cache
+ */
+ dcsp = (Scsp_dcs *)msg->si_tok;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_SOL_RSP, (Scsp_msg *)0,
+ msg);
+ break;
+ case SCSP_UPDATE_REQ:
+ /*
+ * Pass the update request to the FSMs for all
+ * DCSs associated with the server
+ */
+ if (ssp->ss_state == SCSP_SS_ACTIVE) {
+ for (dcsp = ssp->ss_dcs; dcsp;
+ dcsp = dcsp->sd_next) {
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_UPD_REQ,
+ (Scsp_msg *)0, msg);
+ }
+ }
+ break;
+ case SCSP_UPDATE_RSP:
+ /*
+ * Pass the update response to the FSM for the
+ * DCS associated with the request
+ */
+ dcsp = (Scsp_dcs *)msg->si_tok;
+ rc = scsp_cfsm(dcsp, SCSP_CIFSM_UPD_RSP,
+ (Scsp_msg *)0, msg);
+ break;
+ default:
+ scsp_log(LOG_ERR, "invalid message type %d from server",
+ msg->si_type);
+ return(EINVAL);
+ }
+
+ free(msg);
+ return(0);
+}
+
+
+/*
+ * Send a Cache Indication to a server
+ *
+ * Arguments:
+ * ssp pointer to server block block
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_send_cache_ind(ssp)
+ Scsp_server *ssp;
+{
+ int rc;
+ Scsp_if_msg *msg;
+
+ /*
+ * Get storage for a server interface message
+ */
+ msg = calloc(1, sizeof(Scsp_if_msg));
+ if (msg == NULL)
+ scsp_mem_err("scsp_send_cache_ind: sizeof(Scsp_if_msg)");
+ /*
+ * Fill out the message
+ */
+ msg->si_type = SCSP_CACHE_IND;
+ msg->si_proto = ssp->ss_pid;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+ msg->si_tok = (u_long)ssp;
+
+ /*
+ * Send the message
+ */
+ rc = scsp_if_sock_write(ssp->ss_sock, msg);
+ free(msg);
+ return(rc);
+}
+
+
+/*
+ * Read data from a pending server connection
+ *
+ * Arguments:
+ * psp pointer to pending block that has data
+ *
+ * Returns:
+ * 0 success
+ * else errno indicating reason for failure
+ *
+ */
+int
+scsp_pending_read(psp)
+ Scsp_pending *psp;
+
+{
+ int rc;
+ Scsp_server *ssp;
+ Scsp_if_msg *msg;
+
+ /*
+ * Read the message from the pending socket
+ */
+ msg = scsp_if_sock_read(psp->sp_sock);
+ if (!msg) {
+ rc = errno;
+ goto pending_read_fail;
+ }
+
+ /*
+ * Make sure this is configuration data
+ */
+ if (msg->si_type != SCSP_CFG_REQ) {
+ scsp_log(LOG_ERR, "invalid message type %d from pending server",
+ msg->si_type);
+ rc = EINVAL;
+ goto pending_read_fail;
+ }
+
+ /*
+ * Find the server this message is for
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (strcmp(ssp->ss_intf, msg->si_cfg.atmarp_netif) == 0)
+ break;
+ }
+ if (!ssp) {
+ scsp_log(LOG_ERR, "refused connection from server for %s",
+ msg->si_cfg.atmarp_netif);
+ rc = EINVAL;
+ goto config_reject;
+ }
+
+ /*
+ * Make sure the server is ready to go
+ */
+ rc = scsp_get_server_info(ssp);
+ if (rc) {
+ goto config_reject;
+ }
+
+ /*
+ * Save the socket
+ */
+ ssp->ss_sock = psp->sp_sock;
+ ssp->ss_state = SCSP_SS_CFG;
+ UNLINK(psp, Scsp_pending, scsp_pending_head, sp_next);
+ free(psp);
+
+ /*
+ * Listen for connections from the server's DCSs
+ */
+ rc = scsp_dcs_listen(ssp);
+ if (rc < 0) {
+ rc = errno;
+ goto config_reject;
+ }
+
+ /*
+ * Respond to the configuration message
+ */
+ msg->si_type = SCSP_CFG_RSP;
+ msg->si_rc = SCSP_RSP_OK;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+ rc = scsp_if_sock_write(ssp->ss_sock, msg);
+ if (rc) {
+ goto config_error;;
+ }
+
+ /*
+ * Ask the server to send us its cache
+ */
+ rc = scsp_send_cache_ind(ssp);
+ if (rc) {
+ goto config_error;
+ }
+
+ free(msg);
+ return(0);
+
+config_reject:
+ /*
+ * Respond to the configuration message
+ */
+ msg->si_type = SCSP_CFG_RSP;
+ msg->si_rc = SCSP_RSP_REJ;
+ msg->si_len = sizeof(Scsp_if_msg_hdr);
+ (void)scsp_if_sock_write(ssp->ss_sock, msg);
+
+config_error:
+ if (ssp->ss_sock != -1) {
+ (void)close(ssp->ss_sock);
+ ssp->ss_sock = -1;
+ }
+ if (ssp->ss_dcs_lsock != -1) {
+ (void)close(ssp->ss_dcs_lsock);
+ ssp->ss_sock = -1;
+ }
+ ssp->ss_state = SCSP_SS_NULL;
+ free(msg);
+
+ return(rc);
+
+pending_read_fail:
+ /*
+ * Close the socket and free the pending read block
+ */
+ (void)close(psp->sp_sock);
+ UNLINK(psp, Scsp_pending, scsp_pending_head, sp_next);
+ free(psp);
+ if (msg)
+ free(msg);
+ return(rc);
+}
diff --git a/usr.sbin/atm/scspd/scsp_subr.c b/usr.sbin/atm/scspd/scsp_subr.c
new file mode 100644
index 0000000..58a5abf
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_subr.c
@@ -0,0 +1,1113 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP subroutines
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sigmgr.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+#include <netatm/uni/unisig_var.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Hash an SCSP cache key
+ *
+ * Arguments:
+ * ckp pointer to an SCSP cache key structure
+ *
+ * Returns:
+ * hashed value
+ *
+ */
+int
+scsp_hash(ckp)
+ Scsp_ckey *ckp;
+{
+ int i, j, h;
+
+ /*
+ * Turn cache key into a positive integer
+ */
+ h = 0;
+ for (i = ckp->key_len-1, j = 0;
+ i > 0 && j < sizeof(int);
+ i--, j++)
+ h = (h << 8) + ckp->key[i];
+ h = abs(h);
+
+ /*
+ * Return the hashed value
+ */
+ return(h % SCSP_HASHSZ);
+}
+
+
+/*
+ * Compare two SCSP IDs
+ *
+ * Arguments:
+ * id1p pointer to an SCSP ID structure
+ * id2p pointer to an SCSP ID structure
+ *
+ * Returns:
+ * < 0 id1 is less than id2
+ * 0 id1 and id2 are equal
+ * > 0 id1 is greater than id2
+ *
+ */
+int
+scsp_cmp_id(id1p, id2p)
+ Scsp_id *id1p;
+ Scsp_id *id2p;
+{
+ int diff, i;
+
+ /*
+ * Compare the two IDs, byte for byte
+ */
+ for (i = 0; i < id1p->id_len && i < id2p->id_len; i++) {
+ diff = id1p->id[i] - id2p->id[i];
+ if (diff) {
+ return(diff);
+ }
+ }
+
+ /*
+ * IDs are equal. If lengths differ, the longer ID is
+ * greater than the shorter.
+ */
+ return(id1p->id_len - id2p->id_len);
+}
+
+
+/*
+ * Compare two SCSP cache keys
+ *
+ * Arguments:
+ * ck1p pointer to an SCSP cache key structure
+ * ck2p pointer to an SCSP cache key structure
+ *
+ * Returns:
+ * < 0 ck1 is less than ck2
+ * 0 ck1 and ck2 are equal
+ * > 0 ck1 is greater than ck2
+ *
+ */
+int
+scsp_cmp_key(ck1p, ck2p)
+ Scsp_ckey *ck1p;
+ Scsp_ckey *ck2p;
+{
+ int diff, i;
+
+ /*
+ * Compare the two keys, byte for byte
+ */
+ for (i = 0; i < ck1p->key_len && i < ck2p->key_len; i++) {
+ diff = ck1p->key[i] - ck2p->key[i];
+ if (diff)
+ return(diff);
+ }
+
+ /*
+ * Keys are equal. If lengths differ, the longer key is
+ * greater than the shorter.
+ */
+ return(ck1p->key_len - ck2p->key_len);
+}
+
+
+/*
+ * Check whether the host system is an ATMARP server for
+ * the LIS associated with a given interface
+ *
+ * Arguments:
+ * netif pointer to the network interface name
+ *
+ * Returns:
+ * 1 host is a server
+ * 0 host is not a server
+ *
+ */
+int
+scsp_is_atmarp_server(netif)
+ char *netif;
+{
+ int rc;
+ size_t buf_len;
+ struct atminfreq air;
+ struct air_asrv_rsp *asrv_info;
+
+ /*
+ * Get interface information from the kernel
+ */
+ strcpy(air.air_int_intf, netif);
+ air.air_opcode = AIOCS_INF_ASV;
+ buf_len = do_info_ioctl(&air, sizeof(struct air_asrv_rsp));
+ if ((ssize_t)buf_len == -1)
+ return(0);
+
+ /*
+ * Check the interface's ATMARP server address
+ */
+ asrv_info = (struct air_asrv_rsp *) air.air_buf_addr;
+ rc = (asrv_info->asp_addr.address_format == T_ATM_ABSENT) &&
+ (asrv_info->asp_subaddr.address_format ==
+ T_ATM_ABSENT);
+ free(asrv_info);
+ return(rc);
+}
+
+
+/*
+ * Make a copy of a cache summary entry
+ *
+ * Arguments:
+ * csep pointer to CSE entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to new CSE entry
+ *
+ */
+Scsp_cse *
+scsp_dup_cse(csep)
+ Scsp_cse *csep;
+{
+ Scsp_cse *dupp;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ dupp = malloc(sizeof(Scsp_cse));
+ if (dupp == NULL)
+ scsp_mem_err("scsp_dup_cse: sizeof(Scsp_cse)");
+
+ /*
+ * Copy data to the duplicate
+ */
+ bcopy(csep, dupp, sizeof(Scsp_cse));
+ dupp->sc_next = (Scsp_cse *)0;
+ return(dupp);
+}
+
+
+/*
+ * Make a copy of a CSA or CSAS record
+ *
+ * Arguments:
+ * csap pointer to CSE entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to new CSA or CSAS record
+ *
+ */
+Scsp_csa *
+scsp_dup_csa(csap)
+ Scsp_csa *csap;
+{
+ Scsp_csa *dupp;
+ Scsp_atmarp_csa *adp;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ dupp = malloc(sizeof(Scsp_csa));
+ if (dupp == NULL)
+ scsp_mem_err("scsp_dup_csa: sizeof(Scsp_csa)");
+
+ /*
+ * Copy data to the duplicate
+ */
+ bcopy(csap, dupp, sizeof(Scsp_csa));
+ dupp->next = (Scsp_csa *)0;
+
+ /*
+ * Copy protocol-specific data, if it's present
+ */
+ if (csap->atmarp_data) {
+ adp = malloc(sizeof(Scsp_atmarp_csa));
+ if (adp == NULL)
+ scsp_mem_err("scsp_dup_csa: sizeof(Scsp_atmarp_csa)");
+ bcopy(csap->atmarp_data, adp, sizeof(Scsp_atmarp_csa));
+ dupp->atmarp_data = adp;
+ }
+ return(dupp);
+}
+
+
+/*
+ * Copy a cache summary entry into a CSAS
+ *
+ * Arguments:
+ * csep pointer to CSE entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to CSAS record summarizing the entry
+ *
+ */
+Scsp_csa *
+scsp_cse2csas(csep)
+ Scsp_cse *csep;
+{
+ Scsp_csa *csap;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ csap = calloc(1, sizeof(Scsp_csa));
+ if (csap == NULL)
+ scsp_mem_err("scsp_cse2csas: sizeof(Scsp_csa)");
+
+ /*
+ * Copy data to the CSAS entry
+ */
+ csap->seq = csep->sc_seq;
+ csap->key = csep->sc_key;
+ csap->oid = csep->sc_oid;
+
+ return(csap);
+}
+
+
+/*
+ * Copy an ATMARP cache entry into a cache summary entry
+ *
+ * Arguments:
+ * aap pointer to ATMARP cache entry to copy
+ *
+ * Returns:
+ * 0 copy failed
+ * else pointer to CSE record summarizing the entry
+ *
+ */
+Scsp_cse *
+scsp_atmarp2cse(aap)
+ Scsp_atmarp_msg *aap;
+{
+ Scsp_cse *csep;
+
+ /*
+ * Allocate memory for the duplicate
+ */
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_atmarp2cse: sizeof(Scsp_cse)");
+
+ /*
+ * Copy data to the CSE entry
+ */
+ csep->sc_seq = aap->sa_seq;
+ csep->sc_key = aap->sa_key;
+ csep->sc_oid = aap->sa_oid;
+
+ return(csep);
+}
+
+
+/*
+ * Clean up a DCS block. This routine is called to clear out any
+ * lingering state information when the CA FSM reverts to an 'earlier'
+ * state (Down or Master/Slave Negotiation).
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block for the neighbor
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_dcs_cleanup(dcsp)
+ Scsp_dcs *dcsp;
+{
+ Scsp_cse *csep, *ncsep;
+ Scsp_csa *csap, *next_csap;
+ Scsp_csu_rexmt *rxp, *rx_next;
+
+ /*
+ * Free any CSAS entries waiting to be sent
+ */
+ for (csep = dcsp->sd_ca_csas; csep; csep = ncsep) {
+ ncsep = csep->sc_next;
+ UNLINK(csep, Scsp_cse, dcsp->sd_ca_csas, sc_next);
+ free(csep);
+ }
+
+ /*
+ * Free any entries on the CRL
+ */
+ for (csap = dcsp->sd_crl; csap; csap = next_csap) {
+ next_csap = csap->next;
+ UNLINK(csap, Scsp_csa, dcsp->sd_crl, next);
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free any saved CA message and cancel the CA
+ * retransmission timer
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ dcsp->sd_ca_rexmt_msg = (Scsp_msg *)0;
+ }
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+
+ /*
+ * Free any saved CSU Solicit message and cancel the CSUS
+ * retransmission timer
+ */
+ if (dcsp->sd_csus_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_csus_rexmt_msg);
+ dcsp->sd_csus_rexmt_msg = (Scsp_msg *)0;
+ }
+ HARP_CANCEL(&dcsp->sd_csus_rexmt_t);
+
+ /*
+ * Free any entries on the CSU Request retransmission queue
+ */
+ for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = rx_next) {
+ rx_next = rxp->sr_next;
+ HARP_CANCEL(&rxp->sr_t);
+ for (csap = rxp->sr_csa; csap; csap = next_csap) {
+ next_csap = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+ UNLINK(rxp, Scsp_csu_rexmt, dcsp->sd_csu_rexmt,
+ sr_next);
+ free(rxp);
+ }
+}
+
+
+/*
+ * Delete an SCSP DCS block and any associated information
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block to delete
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_dcs_delete(dcsp)
+ Scsp_dcs *dcsp;
+{
+ Scsp_cse *csep, *next_cse;
+ Scsp_csu_rexmt *rxp, *next_rxp;
+ Scsp_csa *csap, *next_csa;
+
+ /*
+ * Cancel any pending DCS timers
+ */
+ HARP_CANCEL(&dcsp->sd_open_t);
+ HARP_CANCEL(&dcsp->sd_hello_h_t);
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ HARP_CANCEL(&dcsp->sd_ca_rexmt_t);
+ HARP_CANCEL(&dcsp->sd_csus_rexmt_t);
+
+ /*
+ * Unlink the DCS block from the server block
+ */
+ UNLINK(dcsp, Scsp_dcs, dcsp->sd_server->ss_dcs, sd_next);
+
+ /*
+ * Close the VCC to the DCS, if one is open
+ */
+ if (dcsp->sd_sock != -1) {
+ (void)close(dcsp->sd_sock);
+ }
+
+ /*
+ * Free any saved CA message
+ */
+ if (dcsp->sd_ca_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_ca_rexmt_msg);
+ }
+
+ /*
+ * Free any pending CSAs waiting for cache alignment
+ */
+ for (csep = dcsp->sd_ca_csas; csep; csep = next_cse) {
+ next_cse = csep->sc_next;
+ free(csep);
+ }
+
+ /*
+ * Free anything on the cache request list
+ */
+ for (csap = dcsp->sd_crl; csap; csap = next_csa) {
+ next_csa = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free any saved CSUS message
+ */
+ if (dcsp->sd_csus_rexmt_msg) {
+ scsp_free_msg(dcsp->sd_csus_rexmt_msg);
+ }
+
+ /*
+ * Free anything on the CSU Request retransmit queue
+ */
+ for (rxp = dcsp->sd_csu_rexmt; rxp; rxp = next_rxp) {
+ /*
+ * Cancel the retransmit timer
+ */
+ HARP_CANCEL(&rxp->sr_t);
+
+ /*
+ * Free the CSAs to be retransmitted
+ */
+ for (csap = rxp->sr_csa; csap; csap = next_csa) {
+ next_csa = csap->next;
+ SCSP_FREE_CSA(csap);
+ }
+
+ /*
+ * Free the CSU Req retransmission control block
+ */
+ next_rxp = rxp->sr_next;
+ free(rxp);
+ }
+
+ /*
+ * Free the DCS block
+ */
+ free(dcsp);
+}
+
+
+/*
+ * Shut down a server. This routine is called when a connection to
+ * a server is lost. It will clear the server's state without deleting
+ * the server.
+ *
+ * Arguments:
+ * ssp pointer to a server control block
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_server_shutdown(ssp)
+ Scsp_server *ssp;
+{
+ int i;
+ Scsp_dcs *dcsp;
+ Scsp_cse *csep;
+
+ /*
+ * Trace the shutdown
+ */
+ if (scsp_trace_mode & (SCSP_TRACE_IF_MSG | SCSP_TRACE_CFSM)) {
+ scsp_trace("Server %s being shut down\n",
+ ssp->ss_name);
+ }
+
+ /*
+ * Terminate up all the DCS connections and clean
+ * up the control blocks
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = dcsp->sd_next) {
+ if (dcsp->sd_sock != -1) {
+ (void)close(dcsp->sd_sock);
+ dcsp->sd_sock = -1;
+ }
+ HARP_CANCEL(&dcsp->sd_open_t);
+ HARP_CANCEL(&dcsp->sd_hello_h_t);
+ HARP_CANCEL(&dcsp->sd_hello_rcv_t);
+ scsp_dcs_cleanup(dcsp);
+ dcsp->sd_hello_state = SCSP_HFSM_DOWN;
+ dcsp->sd_ca_state = SCSP_CAFSM_DOWN;
+ dcsp->sd_client_state = SCSP_CIFSM_NULL;
+ }
+
+ /*
+ * Clean up the server control block
+ */
+ if (ssp->ss_sock != -1) {
+ (void)close(ssp->ss_sock);
+ ssp->ss_sock = -1;
+ }
+ if (ssp->ss_dcs_lsock != -1) {
+ (void)close(ssp->ss_dcs_lsock);
+ ssp->ss_dcs_lsock = -1;
+ }
+ ssp->ss_state = SCSP_SS_NULL;
+
+ /*
+ * Free the entries in the server's summary cache
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ while (ssp->ss_cache[i]) {
+ csep = ssp->ss_cache[i];
+ UNLINK(csep, Scsp_cse, ssp->ss_cache[i],
+ sc_next);
+ free(csep);
+ }
+ }
+}
+
+
+/*
+ * Delete an SCSP server block and any associated information
+ *
+ * Arguments:
+ * ssp pointer to a server control block to delete
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_server_delete(ssp)
+ Scsp_server *ssp;
+{
+ int i;
+ Scsp_dcs *dcsp, *next_dcs;
+ Scsp_cse *csep, *next_cse;
+
+ /*
+ * Unlink the server block from the chain
+ */
+ UNLINK(ssp, Scsp_server, scsp_server_head, ss_next);
+
+ /*
+ * Free the DCS blocks associated with the server
+ */
+ for (dcsp = ssp->ss_dcs; dcsp; dcsp = next_dcs) {
+ next_dcs = dcsp->sd_next;
+ scsp_dcs_delete(dcsp);
+ }
+
+ /*
+ * Free the entries in the server's summary cache
+ */
+ for (i = 0; i < SCSP_HASHSZ; i++) {
+ for (csep = ssp->ss_cache[i]; csep; csep = next_cse) {
+ next_cse = csep->sc_next;
+ free(csep);
+ }
+ }
+
+ /*
+ * Free the server block
+ */
+ free(ssp->ss_name);
+ free(ssp);
+}
+
+
+/*
+ * Get informtion about a server from the kernel
+ *
+ * Arguments:
+ * ssp pointer to the server block
+ *
+ * Returns:
+ * 0 server info is OK
+ * errno server is not ready
+ *
+ */
+int
+scsp_get_server_info(ssp)
+ Scsp_server *ssp;
+{
+ int i, mtu, rc, sel;
+ size_t len;
+ struct atminfreq air;
+ struct air_netif_rsp *netif_rsp = (struct air_netif_rsp *)0;
+ struct air_int_rsp *intf_rsp = (struct air_int_rsp *)0;
+ struct air_cfg_rsp *cfg_rsp = (struct air_cfg_rsp *)0;
+ struct sockaddr_in *ip_addr;
+ Atm_addr_nsap *anp;
+
+ /*
+ * Make sure we're the server for the interface
+ */
+ if (!scsp_is_atmarp_server(ssp->ss_intf)) {
+ rc = EINVAL;
+ goto server_info_done;
+ }
+
+ /*
+ * Get the IP address and physical interface name
+ * associated with the network interface
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_NIF;
+ strcpy(air.air_netif_intf, ssp->ss_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_netif_rsp));
+ if ((ssize_t)len == -1 || len == 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+ netif_rsp = (struct air_netif_rsp *)air.air_buf_addr;
+
+ ip_addr = (struct sockaddr_in *)&netif_rsp->anp_proto_addr;
+ if (ip_addr->sin_family != AF_INET ||
+ ip_addr->sin_addr.s_addr == 0) {
+ rc = EADDRNOTAVAIL;
+ goto server_info_done;
+ }
+
+ /*
+ * Get the MTU for the network interface
+ */
+ mtu = get_mtu(ssp->ss_intf);
+ if (mtu < 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+
+ /*
+ * Get the ATM address associated with the
+ * physical interface
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_INT;
+ strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_int_rsp));
+ if ((ssize_t)len == -1 || len == 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+ intf_rsp = (struct air_int_rsp *)air.air_buf_addr;
+
+ /*
+ * Make sure we're running UNI signalling
+ */
+ if (intf_rsp->anp_sig_proto != ATM_SIG_UNI30 &&
+ intf_rsp->anp_sig_proto != ATM_SIG_UNI31 &&
+ intf_rsp->anp_sig_proto != ATM_SIG_UNI40) {
+ rc = EINVAL;
+ goto server_info_done;
+ }
+
+ /*
+ * Check the physical interface's state
+ */
+ if (intf_rsp->anp_sig_state != UNISIG_ACTIVE) {
+ rc = EHOSTDOWN;
+ goto server_info_done;
+ }
+
+ /*
+ * Make sure the interface's address is valid
+ */
+ if (intf_rsp->anp_addr.address_format != T_ATM_ENDSYS_ADDR &&
+ !(intf_rsp->anp_addr.address_format ==
+ T_ATM_E164_ADDR &&
+ intf_rsp->anp_subaddr.address_format ==
+ T_ATM_ENDSYS_ADDR)) {
+ rc = EINVAL;
+ goto server_info_done;
+ }
+
+ /*
+ * Find the selector byte value for the interface
+ */
+ for (i=0; i<strlen(ssp->ss_intf); i++) {
+ if (ssp->ss_intf[i] >= '0' &&
+ ssp->ss_intf[i] <= '9')
+ break;
+ }
+ sel = atoi(&ssp->ss_intf[i]);
+
+ /*
+ * Get configuration information associated with the
+ * physical interface
+ */
+ bzero(&air, sizeof(struct atminfreq));
+ air.air_opcode = AIOCS_INF_CFG;
+ strcpy(air.air_int_intf, netif_rsp->anp_phy_intf);
+ len = do_info_ioctl(&air, sizeof(struct air_cfg_rsp));
+ if ((ssize_t)len == -1 || len == 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+ cfg_rsp = (struct air_cfg_rsp *)air.air_buf_addr;
+
+ /*
+ * Update the server entry
+ */
+ bcopy(&ip_addr->sin_addr, ssp->ss_lsid.id, ssp->ss_id_len);
+ ssp->ss_lsid.id_len = ssp->ss_id_len;
+ ssp->ss_mtu = mtu + 8;
+ ATM_ADDR_COPY(&intf_rsp->anp_addr, &ssp->ss_addr);
+ ATM_ADDR_COPY(&intf_rsp->anp_subaddr, &ssp->ss_subaddr);
+ if (ssp->ss_addr.address_format == T_ATM_ENDSYS_ADDR) {
+ anp = (Atm_addr_nsap *)ssp->ss_addr.address;
+ anp->aan_sel = sel;
+ } else if (ssp->ss_addr.address_format == T_ATM_E164_ADDR &&
+ ssp->ss_subaddr.address_format ==
+ T_ATM_ENDSYS_ADDR) {
+ anp = (Atm_addr_nsap *)ssp->ss_subaddr.address;
+ anp->aan_sel = sel;
+ }
+ ssp->ss_media = cfg_rsp->acp_cfg.ac_media;
+ rc = 0;
+
+ /*
+ * Free dynamic data
+ */
+server_info_done:
+ if (netif_rsp)
+ free(netif_rsp);
+ if (intf_rsp)
+ free(intf_rsp);
+ if (cfg_rsp)
+ free(cfg_rsp);
+
+ return(rc);
+}
+
+
+/*
+ * Process a CA message
+ *
+ * Arguments:
+ * dcsp pointer to a DCS control block for the neighbor
+ * cap pointer to the CA part of the received message
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_process_ca(dcsp, cap)
+ Scsp_dcs *dcsp;
+ Scsp_ca *cap;
+{
+ Scsp_csa *csap, *next_csap;
+ Scsp_cse *csep;
+ Scsp_server *ssp = dcsp->sd_server;
+
+ /*
+ * Process CSAS records from the CA message
+ */
+ for (csap = cap->ca_csa_rec; csap; csap = next_csap) {
+ next_csap = csap->next;
+ SCSP_LOOKUP(ssp, &csap->key, csep);
+ if (!csep || (scsp_cmp_id(&csap->oid,
+ &csep->sc_oid) == 0 &&
+ csap->seq > csep->sc_seq)) {
+ /*
+ * CSAS entry not in cache or more
+ * up to date than cache, add it to CRL
+ */
+ UNLINK(csap, Scsp_csa, cap->ca_csa_rec, next);
+ LINK2TAIL(csap, Scsp_csa, dcsp->sd_crl, next);
+ }
+ }
+}
+
+
+/*
+ * Process a Cache Response message from a server
+ *
+ * Arguments:
+ * ssp pointer to the server block
+ * smp pointer to the message
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_process_cache_rsp(ssp, smp)
+ Scsp_server *ssp;
+ Scsp_if_msg *smp;
+{
+ int len;
+ Scsp_atmarp_msg *aap;
+ Scsp_cse *csep;
+
+ /*
+ * Loop through the message, processing each cache entry
+ */
+ len = smp->si_len;
+ len -= sizeof(Scsp_if_msg_hdr);
+ aap = &smp->si_atmarp;
+ while (len > 0) {
+ switch(smp->si_proto) {
+ case SCSP_ATMARP_PROTO:
+ /*
+ * If we already have an entry with this key,
+ * delete it
+ */
+ SCSP_LOOKUP(ssp, &aap->sa_key, csep);
+ if (csep) {
+ SCSP_DELETE(ssp, csep);
+ free(csep);
+ }
+
+ /*
+ * Copy the data from the server to a cache
+ * summary entry
+ */
+ csep = scsp_atmarp2cse(aap);
+
+ /*
+ * Point past this entry
+ */
+ len -= sizeof(Scsp_atmarp_msg);
+ aap++;
+ break;
+ case SCSP_NHRP_PROTO:
+ default:
+ /*
+ * Not implemented yet
+ */
+ return;
+ }
+
+ /*
+ * Add the new summary entry to the cache
+ */
+ SCSP_ADD(ssp, csep);
+ }
+}
+
+
+/*
+ * Propagate a CSA to all the DCSs in the server group except
+ * the one the CSA was received from
+ *
+ * Arguments:
+ * dcsp pointer to a the DCS the CSA came from
+ * csap pointer to a the CSA
+ *
+ * Returns:
+ * 0 success
+ * errno error encountered
+ *
+ */
+int
+scsp_propagate_csa(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ int rc, ret_rc = 0;
+ Scsp_server *ssp = dcsp->sd_server;
+ Scsp_dcs *dcsp1;
+ Scsp_csa *csap1;
+
+ /*
+ * Check the hop count in the CSA
+ */
+ if (csap->hops <= 1)
+ return(0);
+
+ /*
+ * Pass the cache entry on to the server's other DCSs
+ */
+ for (dcsp1 = ssp->ss_dcs; dcsp1; dcsp1 = dcsp1->sd_next) {
+ /*
+ * Skip this DCS if it's the one we got
+ * the entry from
+ */
+ if (dcsp1 == dcsp)
+ continue;
+
+ /*
+ * Copy the CSA
+ */
+ csap1 = scsp_dup_csa(csap);
+
+ /*
+ * Decrement the hop count
+ */
+ csap1->hops--;
+
+ /*
+ * Send the copy of the CSA to the CA FSM for the DCS
+ */
+ rc = scsp_cafsm(dcsp1, SCSP_CAFSM_CACHE_UPD,
+ (void *) csap1);
+ if (rc)
+ ret_rc = rc;
+ }
+
+ return(ret_rc);
+}
+
+
+/*
+ * Update SCSP's cache given a CSA or CSAS
+ *
+ * Arguments:
+ * dcsp pointer to a DCS
+ * csap pointer to a CSA
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_update_cache(dcsp, csap)
+ Scsp_dcs *dcsp;
+ Scsp_csa *csap;
+{
+ Scsp_cse *csep;
+
+ /*
+ * Check whether we already have this in the cache
+ */
+ SCSP_LOOKUP(dcsp->sd_server, &csap->key, csep);
+
+ /*
+ * If we don't already have it and it's not being deleted,
+ * build a new cache summary entry
+ */
+ if (!csep && !csap->null) {
+ /*
+ * Get memory for a new entry
+ */
+ csep = calloc(1, sizeof(Scsp_cse));
+ if (csep == NULL)
+ scsp_mem_err("scsp_update_cache: sizeof(Scsp_cse)");
+
+ /*
+ * Fill out the new cache summary entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_key = csap->key;
+ csep->sc_oid = csap->oid;
+
+ /*
+ * Add the new entry to the cache
+ */
+ SCSP_ADD(dcsp->sd_server, csep);
+ }
+
+ /*
+ * Update or delete the entry
+ */
+ if (csap->null) {
+ /*
+ * The null flag is set--delete the entry
+ */
+ if (csep) {
+ SCSP_DELETE(dcsp->sd_server, csep);
+ free(csep);
+ }
+ } else {
+ /*
+ * Update the existing entry
+ */
+ csep->sc_seq = csap->seq;
+ csep->sc_oid = csap->oid;
+ }
+}
+
+
+/*
+ * Reconfigure SCSP
+ *
+ * Called as the result of a SIGHUP interrupt. Reread the
+ * configuration file and solicit the cache from the server.
+ *
+ * Arguments:
+ * none
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_reconfigure()
+{
+ int rc;
+ Scsp_server *ssp;
+
+ /*
+ * Log a message saying we're reconfiguring
+ */
+ scsp_log(LOG_ERR, "Reconfiguring ...");
+
+ /*
+ * Re-read the configuration file
+ */
+ rc = scsp_config(scsp_config_file);
+ if (rc) {
+ scsp_log(LOG_ERR, "Found %d error%s in configuration file",
+ rc, ((rc == 1) ? "" : "s"));
+ exit(1);
+ }
+
+ /*
+ * If a connection to a server is open, get the cache from
+ * the server
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_sock != -1) {
+ rc = scsp_send_cache_ind(ssp);
+ }
+ }
+}
diff --git a/usr.sbin/atm/scspd/scsp_timer.c b/usr.sbin/atm/scspd/scsp_timer.c
new file mode 100644
index 0000000..82e49fc
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_timer.c
@@ -0,0 +1,265 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Timer processing
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <libatm.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Process an SCSP Open timeout
+ *
+ * The open timer is set when an attempt to open a VCC to a DCS fails.
+ * This routine will be called when the timer fires and will retry
+ * the open. Retries can continue indefinitely.
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_open_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_open_t));
+
+ /*
+ * Retry the connection
+ */
+ if (scsp_dcs_connect(dcsp)) {
+ /*
+ * Connect failed -- we hope the error was temporary
+ * and set the timer to try again later
+ */
+ HARP_TIMER(&dcsp->sd_open_t, SCSP_Open_Interval,
+ scsp_open_timeout);
+ }
+}
+
+
+/*
+ * Process an SCSP Hello timeout
+ *
+ * The Hello timer fires every SCSP_HELLO_Interval seconds. This
+ * routine will notify the Hello FSM when the timer fires.
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_hello_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_hello_h_t));
+
+ /*
+ * Call the Hello FSM
+ */
+ (void)scsp_hfsm(dcsp, SCSP_HFSM_HELLO_T, (Scsp_msg *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP receive timeout
+ *
+ * The receive timer is started whenever the Hello FSM receives a
+ * Hello message from its DCS. If the timer fires, it means that no
+ * Hello messages have been received in the DCS's Hello interval.
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_hello_rcv_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_hello_rcv_t));
+
+ /*
+ * Call the Hello FSM
+ */
+ (void)scsp_hfsm(dcsp, SCSP_HFSM_RCV_T, (void *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP CA retransmit timeout
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_ca_retran_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_ca_rexmt_t));
+
+ /*
+ * Call the CA FSM
+ */
+ (void)scsp_cafsm(dcsp, SCSP_CAFSM_CA_T, (void *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP CSUS retransmit timeout
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_csus_retran_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of DCS entry
+ */
+ dcsp = (Scsp_dcs *) ((caddr_t)stp -
+ (int)(&((Scsp_dcs *)0)->sd_csus_rexmt_t));
+
+ /*
+ * Call the CA FSM
+ */
+ (void)scsp_cafsm(dcsp, SCSP_CAFSM_CSUS_T, (void *)0);
+
+ return;
+}
+
+
+/*
+ * Process an SCSP CSU Req retransmit timeout
+ *
+ * Arguments:
+ * stp pointer to an SCSP timer block
+ *
+ * Returns:
+ * None
+ *
+ */
+void
+scsp_csu_req_retran_timeout(stp)
+ Harp_timer *stp;
+{
+ Scsp_csu_rexmt *rxp;
+ Scsp_dcs *dcsp;
+
+ /*
+ * Back off to start of CSU Request retransmission entry
+ */
+ rxp = (Scsp_csu_rexmt *) ((caddr_t)stp -
+ (int)(&((Scsp_csu_rexmt *)0)->sr_t));
+ dcsp = rxp->sr_dcs;
+
+ /*
+ * Call the CA FSM
+ */
+ (void)scsp_cafsm(dcsp, SCSP_CAFSM_CSU_T, (void *)rxp);
+
+ return;
+}
diff --git a/usr.sbin/atm/scspd/scsp_var.h b/usr.sbin/atm/scspd/scsp_var.h
new file mode 100644
index 0000000..fbbddc7
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_var.h
@@ -0,0 +1,461 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP message formats
+ *
+ */
+
+#ifndef _SCSP_SCSP_VAR_H
+#define _SCSP_SCSP_VAR_H
+
+
+/*
+ * Protocol constants
+ */
+#define SCSP_Open_Interval 30
+#define SCSP_HELLO_Interval 3
+#define SCSP_HELLO_DF 3
+#define SCSP_CAReXmitInterval 3
+#define SCSP_CSUSReXmitInterval 3
+#define SCSP_CSA_HOP_CNT 3
+#define SCSP_CSUReXmitInterval 2
+#define SCSP_CSUReXmitMax 5
+
+
+/*
+ * Operational constants
+ */
+#define SCSPD_CONFIG "/etc/scspd.conf"
+#define SCSPD_DIR "/tmp"
+#define SCSPD_DUMP "/tmp/scspd.dump"
+#define SCSP_HASHSZ 19
+#define SCSPD_SOCK_NAME "SCSPD"
+
+
+/*
+ * HELLO finite state machine states
+ */
+#define SCSP_HFSM_DOWN 0
+#define SCSP_HFSM_WAITING 1
+#define SCSP_HFSM_UNI_DIR 2
+#define SCSP_HFSM_BI_DIR 3
+#define SCSP_HFSM_STATE_CNT SCSP_HFSM_BI_DIR + 1
+
+
+/*
+ * HELLO finite state machine events
+ */
+#define SCSP_HFSM_VC_ESTAB 0
+#define SCSP_HFSM_VC_CLOSED 1
+#define SCSP_HFSM_HELLO_T 2
+#define SCSP_HFSM_RCV_T 3
+#define SCSP_HFSM_RCVD 4
+#define SCSP_HFSM_EVENT_CNT SCSP_HFSM_RCVD + 1
+
+
+/*
+ * Cache Alignment finite state machine states
+ */
+#define SCSP_CAFSM_DOWN 0
+#define SCSP_CAFSM_NEG 1
+#define SCSP_CAFSM_MASTER 2
+#define SCSP_CAFSM_SLAVE 3
+#define SCSP_CAFSM_UPDATE 4
+#define SCSP_CAFSM_ALIGNED 5
+#define SCSP_CAFSM_STATE_CNT SCSP_CAFSM_ALIGNED + 1
+
+
+/*
+ * Cache Alignment finite state machine events
+ */
+#define SCSP_CAFSM_HELLO_UP 0
+#define SCSP_CAFSM_HELLO_DOWN 1
+#define SCSP_CAFSM_CA_MSG 2
+#define SCSP_CAFSM_CSUS_MSG 3
+#define SCSP_CAFSM_CSU_REQ 4
+#define SCSP_CAFSM_CSU_REPLY 5
+#define SCSP_CAFSM_CA_T 6
+#define SCSP_CAFSM_CSUS_T 7
+#define SCSP_CAFSM_CSU_T 8
+#define SCSP_CAFSM_CACHE_UPD 9
+#define SCSP_CAFSM_CACHE_RSP 10
+#define SCSP_CAFSM_EVENT_CNT SCSP_CAFSM_CACHE_RSP + 1
+
+
+/*
+ * Client Interface finite state machine states
+ */
+#define SCSP_CIFSM_NULL 0
+#define SCSP_CIFSM_SUM 1
+#define SCSP_CIFSM_UPD 2
+#define SCSP_CIFSM_ALIGN 3
+#define SCSP_CIFSM_STATE_CNT SCSP_CIFSM_ALIGN + 1
+
+
+/*
+ * Client Interface finite state machine events
+ */
+#define SCSP_CIFSM_CA_DOWN 0
+#define SCSP_CIFSM_CA_SUMM 1
+#define SCSP_CIFSM_CA_UPD 2
+#define SCSP_CIFSM_CA_ALIGN 3
+#define SCSP_CIFSM_SOL_RSP 4
+#define SCSP_CIFSM_UPD_REQ 5
+#define SCSP_CIFSM_UPD_RSP 6
+#define SCSP_CIFSM_CSU_REQ 7
+#define SCSP_CIFSM_CSU_REPLY 8
+#define SCSP_CIFSM_CSU_SOL 9
+#define SCSP_CIFSM_EVENT_CNT SCSP_CIFSM_CSU_SOL + 1
+
+
+/*
+ * Server connection states (not part of any FSM)
+ */
+#define SCSP_SS_NULL 0
+#define SCSP_SS_CFG 1
+#define SCSP_SS_ACTIVE 2
+
+
+/*
+ * Hash a cache key
+ *
+ * key pointer to an Scsp_ckey structure
+ */
+#define SCSP_HASH(key) scsp_hash((key))
+
+
+/*
+ * Add a cache summary entry to a client's cache summary
+ *
+ * cpp pointer to a server control block
+ * key pointer to an Scsp_cse structure
+ */
+#define SCSP_ADD(cpp, key) \
+{ \
+ Scsp_cse **c; \
+ c = &(cpp)->ss_cache[SCSP_HASH(&(key)->sc_key)]; \
+ LINK2TAIL((key), Scsp_cse, *c, sc_next); \
+}
+
+
+/*
+ * Delete a cache summary entry from a client's cache summary
+ *
+ * cpp pointer to a server control block
+ * s pointer to an Scsp_cse structure
+ */
+#define SCSP_DELETE(cpp, s) \
+{ \
+ Scsp_cse **c; \
+ c = &(cpp)->ss_cache[SCSP_HASH(&(s)->sc_key)]; \
+ UNLINK((s), Scsp_cse, *c, sc_next); \
+}
+
+
+/*
+ * Search a client's cache summary for a given key
+ *
+ * cpp pointer to a server control block
+ * key pointer to an Scsp_ckey structure to find
+ * s Scsp_cse structure pointer to be set
+ */
+#define SCSP_LOOKUP(cpp, key, s) \
+{ \
+ for ((s) = (cpp)->ss_cache[SCSP_HASH(key)]; \
+ (s); \
+ (s) = (s)->sc_next) { \
+ if (scsp_cmp_key((key), &(s)->sc_key) == 0) \
+ break; \
+ } \
+}
+
+
+/*
+ * SCSP pending connection control block
+ *
+ * The pending connection block is used to keep track of server
+ * connections which are open but haven't been identified yet.
+ */
+struct scsp_pending {
+ struct scsp_pending *sp_next;
+ int sp_sock;
+};
+typedef struct scsp_pending Scsp_pending;
+
+
+/*
+ * SCSP Server instance control block
+ */
+struct scsp_server {
+ struct scsp_server *ss_next; /* Server chain */
+ char *ss_name; /* Server name */
+ char ss_intf[IFNAMSIZ]; /* Interface */
+ Atm_media ss_media; /* Physical comm medium */
+ char ss_state; /* Server connection state */
+ u_long ss_pid; /* Protocol ID */
+ int ss_id_len; /* ID length */
+ int ss_ckey_len; /* Cache key length */
+ u_long ss_sgid; /* Server group ID */
+ u_long ss_fid; /* Family ID */
+ int ss_sock; /* Socket to client */
+ int ss_dcs_lsock; /* DCS listen socket */
+ Scsp_id ss_lsid; /* Local Server ID */
+ Atm_addr ss_addr; /* Local ATM addr */
+ Atm_addr ss_subaddr; /* Local ATM subaddr */
+ int ss_mtu; /* Interface MTU */
+ int ss_mark;
+ struct scsp_dcs *ss_dcs; /* Ptr to list of DCSs */
+ struct scsp_cse *ss_cache[SCSP_HASHSZ]; /* Client's cache */
+};
+typedef struct scsp_server Scsp_server;
+
+
+/*
+ * SCSP client cache summary entry control block
+ */
+struct scsp_cse {
+ struct scsp_cse *sc_next; /* Next on chain */
+ long sc_seq; /* CSA sequence no */
+ Scsp_ckey sc_key; /* Cache key */
+ Scsp_id sc_oid; /* Origin ID */
+};
+typedef struct scsp_cse Scsp_cse;
+
+
+/*
+ * CSU Request retransmission control block
+ */
+struct scsp_csu_rexmt {
+ struct scsp_csu_rexmt *sr_next; /* Next rexmit block */
+ struct scsp_dcs *sr_dcs; /* DCS block */
+ Scsp_csa *sr_csa; /* CSAs for rexmit */
+ Harp_timer sr_t; /* Rexmit timer */
+};
+typedef struct scsp_csu_rexmt Scsp_csu_rexmt;
+
+
+/*
+ * SCSP DCS control block
+ */
+struct scsp_dcs {
+ struct scsp_dcs *sd_next; /* DCS chain */
+ Scsp_server *sd_server; /* Local server */
+ Scsp_id sd_dcsid; /* DCS ID */
+ Atm_addr sd_addr; /* DCS ATM address */
+ Atm_addr sd_subaddr; /* DCS ATM subaddress */
+ int sd_sock; /* Socket to DCS */
+ Harp_timer sd_open_t; /* Open VCC retry timer */
+ int sd_hello_state; /* Hello FSM state */
+ int sd_hello_int; /* Hello interval */
+ int sd_hello_df; /* Hello dead factor */
+ int sd_hello_rcvd; /* Hello msg received */
+ Harp_timer sd_hello_h_t; /* Hello timer */
+ Harp_timer sd_hello_rcv_t; /* Hello receive timer */
+ int sd_ca_state; /* CA FSM state */
+ long sd_ca_seq; /* CA sequence number */
+ int sd_ca_rexmt_int; /* CA rexmit interval */
+ Scsp_msg *sd_ca_rexmt_msg; /* Saved CA msg */
+ Scsp_cse *sd_ca_csas; /* CSAS still to send */
+ Harp_timer sd_ca_rexmt_t; /* CA rexmit timer */
+ int sd_csus_rexmt_int; /* CSUS rexmit int */
+ Scsp_csa *sd_crl; /* Cache req list */
+ Scsp_msg *sd_csus_rexmt_msg; /* Saved CSUS msg */
+ Harp_timer sd_csus_rexmt_t; /* CSUS rexmit timer */
+ int sd_hops; /* CSA hop count */
+ Scsp_csa *sd_csu_ack_pend; /* CSUs to be ACKed */
+ Scsp_csa *sd_csu_ack; /* CSUs ACKed */
+ int sd_csu_rexmt_int; /* CSU Req rxmt time */
+ int sd_csu_rexmt_max; /* CSU Req rxmt limit */
+ Scsp_csu_rexmt *sd_csu_rexmt; /* CSU Req rxmt queue */
+ int sd_client_state; /* Client I/F state */
+};
+typedef struct scsp_dcs Scsp_dcs;
+
+/*
+ * Trace options
+ */
+#define SCSP_TRACE_HFSM 1 /* Trace the Hello FSM */
+#define SCSP_TRACE_CAFSM 2 /* Trace the CA FSM */
+#define SCSP_TRACE_CFSM 4 /* Trace the server I/F FSM */
+#define SCSP_TRACE_HELLO_MSG 8 /* Trace Hello protocol msgs */
+#define SCSP_TRACE_CA_MSG 16 /* Trace CA protocol msgs */
+#define SCSP_TRACE_IF_MSG 32 /* Trace server I/F msgs */
+
+
+/*
+ * Global variables
+ */
+extern char *prog;
+extern FILE *cfg_file;
+extern int parse_line;
+extern char *scsp_config_file;
+extern FILE *scsp_log_file;
+extern int scsp_log_syslog;
+extern Scsp_server *scsp_server_head;
+extern Scsp_pending *scsp_pending_head;
+extern int scsp_max_socket;
+extern int scsp_debug_mode;
+extern int scsp_trace_mode;
+extern FILE *scsp_trace_file;
+
+
+/*
+ * Executable functions
+ */
+/* scsp_cafsm.c */
+extern int scsp_cafsm(Scsp_dcs *, int, void *);
+
+/* scsp_config.c */
+extern int scsp_config(char *);
+extern int start_dcs(void);
+extern int finish_dcs(void);
+extern int set_dcs_addr(char *, char *);
+extern int set_dcs_ca_rexmit(int);
+extern int set_dcs_csus_rexmit(int);
+extern int set_dcs_csu_rexmit(int);
+extern int set_dcs_csu_rexmit_max(int);
+extern int set_dcs_hello_df(int);
+extern int set_dcs_hello_int(int);
+extern int set_dcs_hops(int);
+extern int set_dcs_id(char *);
+extern int set_intf(char *);
+extern int set_protocol(int);
+extern int set_server_group(int);
+extern int start_server(char *);
+extern int finish_server(void);
+extern int set_log_file(char *);
+
+/* scsp_config_lex.c */
+extern int yylex(void);
+
+/* scsp_config_parse.y */
+#if __STDC__
+extern void parse_error(const char *, ...);
+#else
+extern void parse_error(char *, va_alist);
+#endif
+
+/* scsp_hfsm.c */
+extern int scsp_hfsm(Scsp_dcs *, int, Scsp_msg *);
+
+/* scsp_if.c */
+extern int scsp_cfsm(Scsp_dcs *, int, Scsp_msg *, Scsp_if_msg *);
+
+/* scsp_input.c */
+extern void scsp_free_msg(Scsp_msg *);
+extern Scsp_msg *scsp_parse_msg(char *, int);
+
+/* scsp_log.c */
+#if __STDC__
+extern void scsp_log(const int, const char *, ...);
+extern void scsp_trace(const char *, ...);
+#else
+extern void scsp_log(int, char *, va_alist);
+extern void scsp_trace(const char *, va_alist);
+#endif
+extern void scsp_open_trace();
+extern void scsp_trace_msg(Scsp_dcs *, Scsp_msg *, int);
+extern void scsp_mem_err(char *);
+
+/* scsp_msg.c */
+extern void scsp_csus_ack(Scsp_dcs *, Scsp_msg *);
+extern int scsp_send_ca(Scsp_dcs *);
+extern int scsp_send_csus(Scsp_dcs *);
+extern int scsp_send_csu_req(Scsp_dcs *, Scsp_csa *);
+extern int scsp_send_csu_reply(Scsp_dcs *, Scsp_csa *);
+extern int scsp_send_hello(Scsp_dcs *);
+
+/* scsp_output.c */
+extern int scsp_format_msg(Scsp_dcs *, Scsp_msg *, char **);
+extern int scsp_send_msg(Scsp_dcs *, Scsp_msg *);
+
+/* scsp_print.c */
+extern char *format_hfsm_state(int);
+extern char *format_hfsm_event(int);
+extern char *format_cafsm_state(int);
+extern char *format_cafsm_event(int);
+extern char *format_cifsm_state(int);
+extern char *format_cifsm_event(int);
+extern void print_scsp_cse(FILE *, Scsp_cse *);
+extern void print_scsp_msg(FILE *, Scsp_msg *);
+extern void print_scsp_if_msg(FILE *, Scsp_if_msg *);
+extern void print_scsp_pending(FILE *, Scsp_pending *);
+extern void print_scsp_server(FILE *, Scsp_server *);
+extern void print_scsp_dcs(FILE *, Scsp_dcs *);
+extern void print_scsp_dump();
+
+/* scsp_socket.c */
+extern Scsp_dcs * scsp_find_dcs(int);
+extern Scsp_server * scsp_find_server(int);
+extern int scsp_dcs_connect(Scsp_dcs *);
+extern int scsp_dcs_listen(Scsp_server *);
+extern Scsp_dcs * scsp_dcs_accept(Scsp_server *);
+extern int scsp_dcs_read(Scsp_dcs *);
+extern int scsp_server_listen();
+extern int scsp_server_accept(int);
+extern Scsp_if_msg * scsp_if_sock_read(int);
+extern int scsp_if_sock_write(int, Scsp_if_msg *);
+extern int scsp_server_read(Scsp_server *);
+extern int scsp_send_cache_ind(Scsp_server *);
+extern int scsp_pending_read(Scsp_pending *);
+
+/* scsp_subr.c */
+extern int scsp_hash(Scsp_ckey *);
+extern int scsp_cmp_id(Scsp_id *, Scsp_id *);
+extern int scsp_cmp_key(Scsp_ckey *, Scsp_ckey *);
+extern int scsp_is_atmarp_server(char *);
+extern Scsp_cse * scsp_dup_cse(Scsp_cse *);
+extern Scsp_csa * scsp_dup_csa(Scsp_csa *);
+extern Scsp_csa * scsp_cse2csas(Scsp_cse *);
+extern void scsp_dcs_cleanup(Scsp_dcs *);
+extern void scsp_dcs_delete(Scsp_dcs *);
+extern void scsp_server_shutdown(Scsp_server *);
+extern void scsp_server_delete(Scsp_server *);
+extern int scsp_get_server_info(Scsp_server *);
+extern void scsp_process_ca(Scsp_dcs *, Scsp_ca *);
+extern void scsp_process_cache_rsp(Scsp_server *, Scsp_if_msg *);
+extern int scsp_propagate_csa( Scsp_dcs *, Scsp_csa *);
+extern void scsp_update_cache( Scsp_dcs *, Scsp_csa *);
+extern void scsp_reconfigure();
+
+/* scsp_timer.c */
+extern void scsp_open_timeout(Harp_timer *);
+extern void scsp_hello_timeout(Harp_timer *);
+extern void scsp_hello_rcv_timeout(Harp_timer *);
+extern void scsp_ca_retran_timeout(Harp_timer *);
+extern void scsp_csus_retran_timeout(Harp_timer *);
+extern void scsp_csu_req_retran_timeout(Harp_timer *);
+
+
+
+#endif /* _SCSP_SCSP_VAR_H */
diff --git a/usr.sbin/atm/scspd/scspd.8 b/usr.sbin/atm/scspd/scspd.8
new file mode 100644
index 0000000..d369f61
--- /dev/null
+++ b/usr.sbin/atm/scspd/scspd.8
@@ -0,0 +1,633 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" @(#) $FreeBSD$
+.\"
+.\"
+.Dd August 21, 1998
+.Dt SCSPD 8
+.Os
+.Sh NAME
+.Nm scspd
+.Nd "SCSP daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Aq Ar cfg\-file
+.Op Fl d
+.Op Fl T Ns Aq Ar options
+.Sh DESCRIPTION
+The
+.Nm
+utility is an implementation of the Server Cache Synchronization
+Protocol (SCSP) for the Host ATM Research Platform (HARP)
+networking software.
+The
+.Nm
+utility synchronizes the cache(s) of server(s)
+running on a host with the caches of servers on remote hosts.
+SCSP is defined for a number of different protocols, but the present
+version of
+.Nm
+only supports ATMARP.
+.Pp
+By using
+.Nm
+and
+.Xr atmarpd 8 ,
+one can provide multiple
+ATMARP servers in a single ATM LIS.
+This might be useful, for example, when a LIS consists of a number of
+local-area ATM networks connected by long-distance links.
+Each local-area network could have its own ATMARP server, with all the
+servers' caches being synchronized by SCSP.
+Then, if a long-distance link fails, hosts on a local-area network
+will still have connectivity to other local hosts (since they all use
+the local ATMARP server); when the long-distance link is restored,
+SCSP will re-synchronize the servers' caches, restoring
+connectivity to remote hosts.
+Both
+.Nm
+and
+.Xr atmarpd 8
+must be running before any ATMARP
+cache synchronization can take place.
+.Pp
+The
+.Nm
+utility implements SCSP as specified in RFC 2334,
+.%T "Server Cache Synchronization Protocol (SCSP)"
+and
+.Pa draft\-ietf\-ion\-scspd\-atmarpd\-00.txt ,
+.%T "A Distributed ATMARP Service using SCSP" .
+.Pp
+When
+.Nm
+starts, it parses its command line and puts
+itself into the background.
+.Sh TERMINOLOGY
+Some of the vocabulary associated with SCSP can be confusing.
+In this document, the following definitions are used:
+.Pp
+.Em "Client server"
+or
+.Em "local server"
+means the server running on
+the same host as
+.Nm
+whose cache is to be synchronized with that
+of one or more remote servers.
+When the word
+.Em server
+is used alone, it means
+.Em "client server" .
+.Pp
+.Em "Remote server"
+means a server running on some host other than
+the one where
+.Nm
+is running.
+.Pp
+.Em "Directly Connected Server"
+(DCS) means a remote server that
+.Nm
+communicates with directly.
+The remote server will also be running an implementation of SCSP.
+.Pp
+.Em "Cache Alignment"
+(CA) has two meanings.
+The Cache Alignment protocol is a part of the SCSP protocol
+specification, and the Cache Alignment finite state machine (FSM)
+is a finite state machine that implements the Cache Alignment
+protocol.
+.Sh OPTIONS
+The command-line options are:
+.Bl -tag -width "-f <cfg\-file>"
+.It Fl f Aq Ar cfg\-file
+Specifies the name of the configuration file.
+If this option is not specified,
+.Nm
+looks for the
+file
+.Pa /etc/scspd.conf .
+.It Fl d
+Specifies that
+.Nm
+is to be run in debug mode.
+In debug mode, the daemon is not put into the background.
+Log messages are written to standard output instead of to
+the log file specified in the configuration file.
+.It Fl T Ns Aq Ar options
+Specifies that
+.Nm
+will trace specified events and messages
+as it executes.
+The
+.Fl T
+flag is followed by one or more of the following
+options:
+.Pp
+.Bl -tag -width 4n -compact
+.It Cm c
+trace
+.Nm Ns 's
+CA Finite State Machine (FSM),
+.It Cm h
+trace
+.Nm Ns 's
+Hello FSM,
+.It Cm i
+trace
+.Nm Ns 's
+Client Interface FSM,
+.It Cm C
+trace CA, CSUS, CSU Request, and CSU Reply messages,
+.It Cm H
+trace Hello messages,
+.It Cm I
+trace interface messages to and from
+.Nm Ns 's
+clients.
+.El
+.El
+.Sh CONFIGURATION
+The configuration file consists of a sequence of configuration
+statements.
+These statements specify information about the servers,
+both local and remote, whose
+caches are to be synchronized by
+.Nm .
+RFC 2334,
+.%T "Server Cache Synchronization Protocol (SCSP)"
+and
+.Pa draft\-ietf\-ion\-scspd\-atmarpd\-00.txt ,
+.%T "A Distributed ATMARP Service using SCSP"
+will be valuable in understanding how to configure
+.Nm .
+.Pp
+A configuration statement other than a comment is terminated by a
+semicolon.
+Some statements contain blocks, delimited by braces
+.No ( Dq Li {
+and
+.Dq Li } ) .
+Configuration statement keywords are not case-sensitive,
+but some parameters (e.g. interface names) are.
+Configuration statements can span multiple lines.
+.Ss Comments
+Three types of comments are allowed:
+.Bl -hang
+.It Sy "# comments" :
+any characters from
+.Dq Li #
+to the end of the line are ignored.
+.It Sy "C comments" :
+any characters between
+.Dq Li /*
+and
+.Dq Li */
+are ignored.
+.It Sy "C++ comments" :
+any characters from
+.Dq Li //
+to the end of the line are ignored.
+.El
+.Ss Statements
+The configuration statements recognized by
+.Nm
+are:
+.Bd -literal
+Server <name> {
+ Protocol <protocol ID>;
+ Netif <if_name>;
+ ServerGroupID <ID>;
+ FamilyID <ID>;
+ DCS {
+ ATMaddr <ATM address>;
+ ID <host>;
+ CAReXmitInt <int>;
+ CSUSReXmitInt <int>;
+ CSUReXmitInt <int>;
+ CSUReXmitMax <cnt>;
+ HelloDead <cnt>;
+ HelloInt <int>;
+ Hops <cnt>;
+ };
+};
+
+Log {
+ File <file name>;
+ Syslog;
+};
+.Ed
+.Pp
+Where a host address needs to be specified in the configuration file,
+either a DNS name or an IP address in dotted decimal format can
+be used.
+.Pp
+ATM addresses are specified as strings of hex digits, with an
+optional leading
+.Dq Li 0x .
+Fields within the address may be separated by periods, but periods
+are for readability only and are ignored.
+ATM addresses are 20 bytes long.
+The full address, including any leading zeroes, must be given.
+For example:
+.Pp
+.Dl "0x47.0005.80.ffe100.0000.f21a.0170.0020481a0170.00"
+.Ss "Server Statement"
+The
+.Ic server
+statement specifies a client server whose cache
+to be synchronized with the caches of other servers
+running on remote hosts.
+There will be one
+.Ic server
+statement in the configuration file
+for each client server whose cache is to be synchronized by
+.Nm .
+The format of the
+.Ic server
+statement is:
+.Bd -ragged -offset indent
+.Ic Server
+.Aq Ar name
+{
+.Aq Ar statements
+};
+.Ed
+.Pp
+A
+.Ar name
+must be specified on the
+.Ic server
+statement, but it is
+not used by
+.Nm .
+It is expected to give a brief description of the server's purpose.
+.Pp
+The
+.Ic server
+statement has several sub-statements
+that specify the details of the
+.Nm Ns 's
+configuration.
+They are:
+.Bl -tag -width indent
+.It Ic Protocol Cm ATMARP ;
+The only protocol supported by the current version of
+.Nm
+is
+.Cm ATMARP .
+The
+.Ic protocol
+statement must always be specified.
+.It Ic Netif Aq Ar intf ;
+The
+.Ic netif
+statement specifies the name of the ATM network
+interface on which a client server is providing service.
+The
+.Ic netif
+statement must always be specified.
+.It Ic ServerGroupID Aq Ar ID ;
+The
+.Ic ServerGroupID
+statement specifies an identifier for the
+group of servers being synchronized by
+.Nm .
+The
+.Ar ID
+is specified as a decimal number in the range 0 - 65,535.
+The server group ID must be the same for all servers whose caches
+are being synchronized by an SCSP session.
+That is, the server group ID for a host must be the same for all
+Directly Connected Servers (DCSs) pointed to within a
+.Ic server
+statement.
+The
+.Ic ServerGroupID
+statement must always be specified.
+.It Ic FamilyID Aq Ar ID ;
+The
+.Ic familyID
+statement specifies an identifier for a family
+of parallel SCSP sessions running between a group of hosts (i.e. a
+set of SCSP sessions with different protocol IDs but the same set
+of servers).
+The
+.Ar ID
+is specified as a decimal number in the range 0 - 65,535.
+The family ID is currently not used by
+.Nm .
+.El
+.Ss "DCS Statement"
+The
+.Ic DCS
+statement is a sub-statement of the
+.Ic server
+statement
+that specifies the characteristics of a Directly Connected Server (DCS).
+The
+.Ic server
+statement will have one
+.Ic DCS
+statement for
+each DCS that
+.Nm
+is to exchange information with.
+The
+.Ic DCS
+statement has a number of sub-statements that specify the
+details of the configuration for the DCS.
+They are:
+.Bl -tag -width indent
+.It Ic ATMaddr Aq Ar ATM\ address ;
+The
+.Ic ATMaddr
+statement specifies the ATM address of the DCS.
+The
+.Ic ATMaddr
+statement must always be specified.
+.It Ic ID Aq Ar host ;
+The
+.Ic ID
+statement specifies the SCSP identifier of the DCS.
+For ATMARP, the ID is the IP address or DNS name associated with the
+ATM interface of the DCS.
+The
+.Ic ID
+statement must always be specified.
+.It Ic CAReXmitInt Aq Ar int ;
+The
+.Ic CAReXmitInt
+statement specifies the interval that is
+allowed to elapse between retransmissions of CA messages.
+If a CA message is sent and an acknowledgement is not received within
+.Ic CAReXmitInt
+seconds, the message will be retransmitted.
+The default value for
+.Ic CAReXmitInt
+is 3 seconds.
+.It Ic CSUSReXmitInt Aq Ar int ;
+The
+.Ic CSUSReXmitInt
+statement specifies the interval that is
+allowed to elapse between retransmissions of CSU Solicit messages.
+When a CSUS message is sent, any Cache State Advertisements (CSAs)
+requested by the CSUS that have
+not been received within
+.Ic CSUSReXmitInt
+seconds will be requested
+again by another CSUS message.
+The default value for
+.Ic CSUSReXmitInt
+is 3 seconds.
+Be careful not to confuse
+.Ic CSUSReXmitInt
+and
+.Ic CSUReXmitInt .
+.It Ic CSUReXmitInt Aq Ar int ;
+The
+.Ic CSUReXmitInt
+statement specifies the interval that is
+allowed to elapse between retransmissions of CSU Request messages.
+When a CSU Request message is sent, any CSAs that are not acknowledged
+by a CSU Reply message within
+.Ic CSUReXmitInt
+seconds will
+be retransmitted.
+The default value for
+.Ic CSUReXmitInt
+is 2 seconds.
+Be careful not to confuse
+.Ic CSUReXmitInt
+and
+.Ic CSUSReXmitInt .
+.It Ic CSUReXmitMax Aq Ar cnt ;
+The
+.Ic CSUReXmitMax
+statement specifies the number of times that
+a CSA will be retransmitted as described above before SCSP gives up
+on the CSA and discards it.
+The default value for
+.Ic CSUReXmitMax
+is 5.
+.It Ic HelloDead Aq Ar cnt ;
+The
+.Ic HelloDead
+statement specifies the Hello Dead Factor that
+will be sent to the DCS in Hello messages.
+A
+.Dq "DCS down"
+condition will be detected when nothing is received from
+a DCS in
+.Ic HelloDead No * Ic HelloInt
+seconds.
+The default value for
+.Ic HelloDead
+is 3.
+.It Ic HelloInt Aq Ar int ;
+The
+.Ic HelloInt
+statement specifies the Hello Interval that
+will be sent to the DCS in Hello messages.
+The default value for
+.Ic HelloInt
+is 3 seconds.
+.It Ic Hops Aq Ar cnt ;
+The
+.Ic Hops
+statement specifies the number of hops (DCS to DCS)
+that will be specified in CSAs originating from the local server.
+This number must be at least as large as the diameter of the
+server group.
+That is, it must be large enough for a CSA to be propagated from
+server to server all the way across the server group.
+The default value for
+.Ic Hops
+is 3.
+.El
+.Ss "Log Statement"
+The
+.Ic log
+statement specifies how
+.Nm
+is to log
+information about its operation.
+The
+.Nm
+utility can write log information to a file, to the system log,
+or both.
+.Bl -tag -width indent
+.It Ic File Aq Ar file\ name ;
+The
+.Ic file
+statement specifies that
+.Nm
+is to write
+its log messages to the named file.
+Log messages will be appended to the end of the file if
+it already exists.
+.It Ic Syslog ;
+The
+.Ic syslog
+statement specifies that
+.Nm
+is to write
+its log messages to the syslog facility.
+The
+.Nm
+utility writes its messages to syslog with a facility code
+of
+.Dv LOG_DAEMON .
+.El
+.Pp
+If no
+.Ic log
+statement is specified,
+.Nm
+writes log
+messages to the system log.
+If both
+.Ic file
+and
+.Ic syslog
+are specified,
+.Nm
+will
+write log messages to both the named file and the system log.
+.Ss Examples
+An example of a simple configuration file for
+.Nm
+might be:
+.Bd -literal -offset indent
+server atmarp_ni0 {
+ protocol ATMARP;
+ netif ni0;
+ ServerGroupID 23;
+ DCS {
+ ID 10.1.1.2;
+ ATMaddr 0x47.0005.80.ffdc00.0000.0002.0001.002048061de7.00;
+ hops 2;
+ };
+};
+.Ed
+.Pp
+This configuration would synchronize the cache of the ATMARP server
+operating on network interface ni0 with the cache of a second server
+running on a host whose IP address is 10.1.1.2.
+Log messages would be written to the system log.
+.Sh SIGNAL PROCESSING
+The following signals can be used to control
+.Nm :
+.Bl -tag -width indent
+.It Dv SIGHUP
+Reread the configuration file and restart
+.Nm .
+.It Dv SIGINT
+Dump debugging information to a file.
+When it receives a
+.Dv SIGINT
+signal,
+.Nm
+dumps a summary of
+its control blocks to a text file (see
+.Sx FILES ) .
+.El
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /etc/scspd.conf
+.Nm
+default configuration file name.
+A different file name can be specified with the
+.Fl f
+command-line
+option.
+.It Xo
+.Sm off
+.Pa /tmp/scspd.
+.Aq Ar pid
+.Pa \&.
+.Aq Ar seq
+.Pa .out
+.Sm on
+.Xc
+debugging information dump file name.
+The
+.Nm
+utility writes a summary of its control blocks to this file
+when it receives a
+.Dv SIGINT
+signal.
+.Aq Ar pid
+is the process ID of the daemon and
+.Aq Ar seq
+is a sequence
+number which is incremented every time a dump is taken.
+.It Xo
+.Sm off
+.Pa /tmp/scspd.
+.Aq Ar pid
+.Pa .trace
+.Sm on
+.Xc
+trace file.
+The
+.Nm
+utility writes trace information to this file if the
+.Fl T
+option is specified on the command line.
+.El
+.Sh SEE ALSO
+.Xr atm 8 ,
+.Xr atmarpd 8
+.Rs
+.%O "RFC 2334"
+.%T "Server Cache Synchronization Protocol (SCSP)"
+.Re
+.Rs
+.%O "draft\-ietf\-ion\-scsp\-atmarpd\-00.txt"
+.%T "A Distributed ATMARP Service Using SCSP"
+.Re
+.Sh BUGS
+If
+.Nm
+terminates and is restarted, there will be a period of
+instability while previously-synchronized cache entries time out and are
+refreshed.
+.Pp
+Please report any bugs to
+.Aq harp\-bugs@magic.net .
+.Sh COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+.Sh AUTHORS
+.An John Cavanaugh ,
+Network Computing Services, Inc.
+.An Mike Spengler ,
+Network Computing Services, Inc.
+.An Joe Thomas ,
+Network Computing Services, Inc.
+.Sh ACKNOWLEDGMENTS
+This software was developed with the support of the Defense
+Advanced Research Projects Agency (DARPA).
diff --git a/usr.sbin/atm/scspd/scspd.c b/usr.sbin/atm/scspd/scspd.c
new file mode 100644
index 0000000..30d7015
--- /dev/null
+++ b/usr.sbin/atm/scspd/scspd.c
@@ -0,0 +1,547 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $FreeBSD$
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP server daemon main line code
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/ttycom.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libatm.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#ifndef lint
+__RCSID("@(#) $FreeBSD$");
+#endif
+
+
+/*
+ * Global variables
+ */
+char *prog;
+char *scsp_config_file = SCSPD_CONFIG;
+FILE *scsp_log_file = (FILE *)0;
+int scsp_log_syslog = 0;
+Scsp_server *scsp_server_head = (Scsp_server *)0;
+Scsp_pending *scsp_pending_head = (Scsp_pending *)0;
+int scsp_max_socket = -1;
+int scsp_debug_mode = 0;
+int scsp_trace_mode = 0;
+
+
+/*
+ * Local variables
+ */
+static int scsp_hup_signal = 0;
+static int scsp_int_signal = 0;
+
+
+/*
+ * SIGHUP signal handler
+ *
+ * Arguments:
+ * sig signal number
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_sighup(sig)
+ int sig;
+{
+ /*
+ * Flag the signal
+ */
+ scsp_hup_signal = 1;
+}
+
+
+/*
+ * SIGINT signal handler
+ *
+ * Arguments:
+ * sig signal number
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+scsp_sigint(sig)
+ int sig;
+{
+ /*
+ * Flag the signal
+ */
+ scsp_int_signal = 1;
+}
+
+
+/*
+ * Process command line parameters
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+void
+initialize(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ char *cp;
+
+ /*
+ * Save program name, ignoring any path components
+ */
+ if ((prog = (char *)strrchr(argv[0], '/')) != NULL)
+ prog++;
+ else
+ prog = argv[0];
+
+ /*
+ * Make sure we're being invoked by the super user
+ */
+ i = getuid();
+ if (i != 0) {
+ fprintf(stderr, "%s: You must be root to run this program\n",
+ prog);
+ exit(1);
+ }
+
+ /*
+ * Check for command-line options
+ */
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-d") == 0) {
+ /*
+ * -d option -- set debug mode
+ */
+ scsp_debug_mode = 1;
+ } else if (strcmp(argv[i], "-f") == 0) {
+ /*
+ * -f option -- set config file name
+ */
+ i++;
+ if (i >= argc) {
+ fprintf(stderr, "%s: Configuration file name missing\n",
+ prog);
+ exit(1);
+ }
+ scsp_config_file = argv[i];
+ } else if (strncmp(argv[i], "-T", 2) == 0) {
+ /*
+ * -T option -- trace options
+ */
+ for (cp = &argv[i][2]; *cp; cp++) {
+ if (*cp == 'c')
+ scsp_trace_mode |= SCSP_TRACE_CAFSM;
+ else if (*cp == 'h')
+ scsp_trace_mode |= SCSP_TRACE_HFSM;
+ else if (*cp == 'i')
+ scsp_trace_mode |= SCSP_TRACE_CFSM;
+ else if (*cp == 'C')
+ scsp_trace_mode |= SCSP_TRACE_CA_MSG;
+ else if (*cp == 'H')
+ scsp_trace_mode |= SCSP_TRACE_HELLO_MSG;
+ else if (*cp == 'I')
+ scsp_trace_mode |= SCSP_TRACE_IF_MSG;
+ else
+ fprintf(stderr, "Invalid trace specification '%c' ignored\n",
+ *cp);
+ }
+ } else {
+ /*
+ * Error -- unrecognized option
+ */
+ fprintf(stderr, "%s: Unrecognized option \"%s\"\n",
+ prog, argv[i]);
+ exit(1);
+ }
+ }
+}
+
+
+/*
+ * Daemon housekeeping
+ *
+ * Arguments:
+ * None
+ *
+ * Returns:
+ * None
+ *
+ */
+static void
+start_daemon()
+
+{
+ int dpid, fd, file_count, rc;
+
+ /*
+ * Ignore selected signals
+ */
+#ifdef SIGTTOU
+ signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+
+ /*
+ * Don't put the daemon into the background if
+ * we're in debug mode
+ */
+ if (scsp_debug_mode)
+ goto daemon_bypass;
+
+ /*
+ * Put the daemon into the background
+ */
+ dpid = fork();
+ if (dpid < 0) {
+ scsp_log(LOG_ERR, "fork failed");
+ abort();
+ }
+ if (dpid > 0) {
+ /*
+ * This is the parent process--just exit and let
+ * the daughter do all the work
+ */
+ exit(0);
+ }
+
+ /*
+ * Disassociate from any controlling terminal
+ */
+ rc = setpgrp(0, getpid());
+ if (rc <0) {
+ scsp_log(LOG_ERR, "can't change process group");
+ exit(1);
+ }
+ fd = open(_PATH_TTY, O_RDWR);
+ if (fd >= 0) {
+ ioctl(fd, TIOCNOTTY, (char *)0);
+ close(fd);
+ }
+
+ /*
+ * Close all open file descriptors
+ */
+ file_count = getdtablesize();
+ for (fd=0; fd<file_count; fd++) {
+ close(fd);
+ }
+
+ /*
+ * Set up timers
+ */
+daemon_bypass:
+ init_timer();
+
+ /*
+ * Move to a safe directory
+ */
+ chdir(SCSPD_DIR);
+
+ /*
+ * Clear the file mode creation mask
+ */
+ umask(0);
+
+
+ /*
+ * Set up signal handlers
+ */
+ if (signal(SIGHUP, scsp_sighup) == SIG_ERR) {
+ scsp_log(LOG_ERR, "SIGHUP signal setup failed");
+ exit(1);
+ }
+
+ if (signal(SIGINT, scsp_sigint) == SIG_ERR) {
+ scsp_log(LOG_ERR, "SIGINT signal setup failed");
+ exit(1);
+ }
+
+ /*
+ * Set up syslog for error logging
+ */
+ if (scsp_log_syslog || !scsp_log_file) {
+ openlog(prog, LOG_PID | LOG_CONS, LOG_DAEMON);
+ }
+ scsp_log(LOG_INFO, "Starting SCSP daemon");
+}
+
+
+/*
+ * Main line code
+ *
+ * Process command line parameters, read configuration file, connect
+ * to configured clients, process data from DCSs.
+ *
+ * Arguments:
+ * argc number of command-line arguments
+ * argv list of pointers to command-line arguments
+ *
+ * Returns:
+ * none
+ *
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int i, rc, scsp_server_lsock;
+ Scsp_server *ssp;
+ Scsp_dcs *dcsp;
+ Scsp_pending *next_psp, *psp;
+ fd_set read_set, write_set, except_set;
+
+ /*
+ * Process command line arguments
+ */
+ initialize(argc, argv);
+
+ /*
+ * Put the daemon into the background
+ */
+ start_daemon();
+
+ /*
+ * Process configuration file
+ */
+ rc = scsp_config(scsp_config_file);
+ if (rc) {
+ scsp_log(LOG_ERR, "Found %d error%s in configuration file",
+ rc, ((rc == 1) ? "" : "s"));
+ exit(1);
+ }
+
+ /*
+ * Open the trace file if we need one
+ */
+ if (scsp_trace_mode) {
+ scsp_open_trace();
+ }
+
+ /*
+ * Listen for connections from clients
+ */
+ scsp_server_lsock = scsp_server_listen();
+ if (scsp_server_lsock == -1) {
+ scsp_log(LOG_ERR, "server listen failed");
+ abort();
+ }
+
+ /*
+ * Main program loop -- we wait for:
+ * a server listen to complete
+ * a DCS listen to complete
+ * a DCS connect to complete
+ * data from a server
+ * data from a DCS
+ */
+ while (1) {
+ /*
+ * Set up the file descriptor sets and select to wait
+ * for input
+ */
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_ZERO(&except_set);
+ FD_SET(scsp_server_lsock, &read_set);
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_dcs_lsock != -1)
+ FD_SET(ssp->ss_dcs_lsock, &read_set);
+ if (ssp->ss_sock != -1)
+ FD_SET(ssp->ss_sock, &read_set);
+ for (dcsp = ssp->ss_dcs; dcsp;
+ dcsp = dcsp->sd_next) {
+ if (dcsp->sd_sock != -1) {
+ if (dcsp->sd_hello_state ==
+ SCSP_HFSM_DOWN )
+ FD_SET(dcsp->sd_sock,
+ &write_set);
+ else
+ FD_SET(dcsp->sd_sock,
+ &read_set);
+ }
+ }
+ }
+ for (psp = scsp_pending_head; psp; psp = psp->sp_next) {
+ FD_SET(psp->sp_sock, &read_set);
+ }
+ rc = select(scsp_max_socket + 1, &read_set,
+ &write_set, &except_set,
+ (struct timeval *)0);
+ if (rc < 0) {
+ /*
+ * Select error--check for possible signals
+ */
+ if (harp_timer_exec) {
+ /*
+ * Timer tick--process it
+ */
+ timer_proc();
+ continue;
+ } else if (scsp_hup_signal) {
+ /*
+ * SIGHUP signal--reconfigure
+ */
+ scsp_hup_signal = 0;
+ scsp_reconfigure();
+ continue;
+ } else if (scsp_int_signal) {
+ /*
+ * SIGINT signal--dump control blocks
+ */
+ print_scsp_dump();
+ scsp_int_signal = 0;
+ continue;
+ } else if (errno == EINTR) {
+ /*
+ * EINTR--just ignore it
+ */
+ continue;
+ } else {
+ /*
+ * Other error--this is a problem
+ */
+ scsp_log(LOG_ERR, "Select failed");
+ abort();
+ }
+ }
+
+ /*
+ * Check the read set for connections from servers
+ */
+ if (FD_ISSET(scsp_server_lsock, &read_set)) {
+ FD_CLR(scsp_server_lsock, &read_set);
+ rc = scsp_server_accept(scsp_server_lsock);
+ }
+
+ /*
+ * Check the write set for new connections to DCSs
+ */
+ for (i = 0; i <= scsp_max_socket; i++) {
+ if (FD_ISSET(i, &write_set)) {
+ FD_CLR(i, &write_set);
+ if ((dcsp = scsp_find_dcs(i)) != NULL) {
+ rc = scsp_hfsm(dcsp,
+ SCSP_HFSM_VC_ESTAB,
+ (Scsp_msg *)0);
+ }
+ }
+ }
+
+ /*
+ * Check the read set for connections from DCSs
+ */
+ for (ssp = scsp_server_head; ssp; ssp = ssp->ss_next) {
+ if (ssp->ss_dcs_lsock != -1 &&
+ FD_ISSET(ssp->ss_dcs_lsock,
+ &read_set)) {
+ FD_CLR(ssp->ss_dcs_lsock, &read_set);
+ dcsp = scsp_dcs_accept(ssp);
+ if (dcsp) {
+ rc = scsp_hfsm(dcsp,
+ SCSP_HFSM_VC_ESTAB,
+ (Scsp_msg *)0);
+ }
+ }
+ }
+
+ /*
+ * Check the read set for data from pending servers
+ */
+ for (psp = scsp_pending_head; psp; psp = next_psp) {
+ next_psp = psp->sp_next;
+ if (FD_ISSET(psp->sp_sock, &read_set)) {
+ FD_CLR(psp->sp_sock, &read_set);
+ rc = scsp_pending_read(psp);
+ }
+ }
+
+ /*
+ * Check the read set for data from servers or DCSs
+ */
+ for (i = 0; i <= scsp_max_socket; i++) {
+ if (FD_ISSET(i, &read_set)) {
+ if ((ssp = scsp_find_server(i)) != NULL) {
+ rc = scsp_server_read(ssp);
+ } else if ((dcsp = scsp_find_dcs(i)) != NULL) {
+ rc = scsp_dcs_read(dcsp);
+ }
+ }
+ }
+ }
+}
diff --git a/usr.sbin/authpf/Makefile b/usr.sbin/authpf/Makefile
new file mode 100644
index 0000000..c4bcd63
--- /dev/null
+++ b/usr.sbin/authpf/Makefile
@@ -0,0 +1,26 @@
+# $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 parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c
+SRCS+= pfctl_radix.c pfctl_osfp.c
+
+CFLAGS+= -I${.CURDIR}/../../contrib/pf/pfctl -Wall -Werror
+
+# XXX ALTQ:
+#CFLAGS+= -DENABLE_ALTQ
+CFLAGS+= -I${.CURDIR}/../../sbin/pfctl/missing
+
+LDADD+= -lm -lmd
+DPADD+= ${LIBM} ${LIBMD}
+
+CLEANFILES+=y.tab.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/Makefile b/usr.sbin/bluetooth/Makefile
new file mode 100644
index 0000000..23c03f7
--- /dev/null
+++ b/usr.sbin/bluetooth/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.5 2003/09/08 02:28:35 max Exp $
+# $FreeBSD$
+
+SUBDIR= \
+ bcmfw \
+ bt3cfw \
+ 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..e601a76
--- /dev/null
+++ b/usr.sbin/bluetooth/bcmfw/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.6 2003/08/14 20:05:58 max Exp $
+# $FreeBSD$
+
+PROG= bcmfw
+MAN= bcmfw.8
+SRCS= bcmfw.c
+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..87c8093
--- /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 BlueFW package.
+.Pp
+Visit
+.Pa http://bluez.sourceforge.net/download/download.html
+for details.
+.Pp
+I am using the following files:
+.Pp
+.Dl "MD5 (BCM2033-MD.hex) = 5580317158d07fc4ace90af04f8e1c73"
+.Dl "MD5 (BCM2033-FW.bin) = a3a0edfcb85029f4a5d0b1c9649b127d"
+.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 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 BUGS
+Most likely.
+Please report if found.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr ubtbcmfw 4 ,
+.Xr ugen 4
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/bcmfw/bcmfw.c b/usr.sbin/bluetooth/bcmfw/bcmfw.c
new file mode 100644
index 0000000..e21c10c
--- /dev/null
+++ b/usr.sbin/bluetooth/bcmfw/bcmfw.c
@@ -0,0 +1,305 @@
+/*
+ * 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/usbdevs.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
+
+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) != 0x2033) {
+ 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..daa0f65
--- /dev/null
+++ b/usr.sbin/bluetooth/bt3cfw/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.5 2003/08/14 20:06:00 max Exp $
+# $FreeBSD$
+
+PROG= bt3cfw
+MAN= bt3cfw.8
+SRCS= bt3cfw.c
+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..74aa6d7
--- /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 BUGS
+Please report if found.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr ng_bt3c 4
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
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..95327df
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8
@@ -0,0 +1,99 @@
+.\" 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 February 13, 2004
+.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
+.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 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 DIAGNOSTICS
+.Ex -std
+.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 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..21539e3
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c
@@ -0,0 +1,208 @@
+/*
+ * 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);
+
+/*
+ * 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:h")) != -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 '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" \
+" 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..78b4105
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/hid.c
@@ -0,0 +1,209 @@
+/*
+ * 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"
+
+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)
+{
+ 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..e39c8ca
--- /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), /* HIDDesctiptorList */
+SDP_ATTR_RANGE( 0x020a, /* HIDBatteryPower */
+ 0x020a),
+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: /* HIDDesctiptorList */
+ if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
+ hid_descriptor = values[i].value;
+ hid_descriptor_length = values[i].vlen;
+ }
+ break;
+
+ case 0x020a: /* 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..c3bab372
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.2 2004/02/13 21:46:20 max Exp $
+# $FreeBSD$
+
+PROG= bthidd
+#MAN= bthidd.8 bthidd.conf.5
+NOMAN= 1
+SRCS= bthidd.c client.c hid.c lexer.l parser.y server.c session.c
+WARNS?= 1
+CFLAGS+= -I${.CURDIR}
+
+DPADD= ${LIBBLUETOOTH} ${LIBSDP}
+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..6be7c7e
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthid_config.h
@@ -0,0 +1,67 @@
+/*
+ * bthid_config.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: bthid_config.h,v 1.3 2004/02/17 22:05:02 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 reserved : 12;
+ 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 *config_file;
+extern char *hids_file;
+
+int 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);
+
+int read_hids_file (void);
+int write_hids_file (void);
+
+#endif /* ndef _BTHID_CONFIG_H_ */
+
diff --git a/usr.sbin/bluetooth/bthidd/bthidd.c b/usr.sbin/bluetooth/bthidd/bthidd.c
new file mode 100644
index 0000000..0782a36
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthidd.c
@@ -0,0 +1,256 @@
+/*
+ * bthidd.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: bthidd.c,v 1.4 2004/02/26 21:48:44 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 "bthidd.h"
+#include "bthid_config.h"
+
+static int write_pid_file (char const *file);
+static int remove_pid_file (char const *file);
+static int elapsed (int tval);
+static void sighandler (int s);
+static void usage (void);
+
+/*
+ * bthidd
+ */
+
+static int done = 0; /* are we done? */
+
+int
+main(int argc, char *argv[])
+{
+ struct bthid_server srv;
+ struct sigaction sa;
+ char const *pid_file = BTHIDD_PIDFILE;
+ int opt, detach, tval;
+
+ memcpy(&srv.bdaddr, NG_HCI_BDADDR_ANY, 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 = NULL;
+
+ 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 */
+ char *ep = NULL;
+
+ tval = strtol(optarg, &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);
+ }
+
+ 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 int
+write_pid_file(char const *file)
+{
+ FILE *pid = NULL;
+
+ 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 int
+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 int
+elapsed(int tval)
+{
+ static struct timeval last = { 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(int 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 bdaddr specify BDADDR 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 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..d9cd015
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthidd.h
@@ -0,0 +1,88 @@
+/*
+ * bthidd.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: bthidd.h,v 1.4 2004/02/26 21:44:20 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 */
+ int cons; /* /dev/consolectl */
+ int ctrl; /* control channel (listen) */
+ int intr; /* interrupt channel (listen) */
+ int 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 */
+ int ctrl; /* control channel */
+ int intr; /* interrupt channel */
+ bdaddr_t bdaddr; /* remote bdaddr */
+ short state; /* session state */
+#define CLOSED 0
+#define W4CTRL 1
+#define W4INTR 2
+#define OPEN 3
+ LIST_ENTRY(bthid_session) next; /* link to next */
+};
+
+typedef struct bthid_session bthid_session_t;
+typedef struct bthid_session * bthid_session_p;
+
+int server_init (bthid_server_p srv);
+void server_shutdown (bthid_server_p srv);
+int server_do (bthid_server_p srv);
+
+int client_rescan (bthid_server_p srv);
+int client_connect (bthid_server_p srv, int fd);
+
+bthid_session_p session_open (bthid_server_p srv, bdaddr_p bdaddr);
+bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr);
+bthid_session_p session_by_fd (bthid_server_p srv, int fd);
+void session_close (bthid_session_p s);
+
+int hid_control (bthid_session_p s, char *data, int len);
+int hid_interrupt (bthid_session_p s, char *data, int 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..cc611bd
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/client.c
@@ -0,0 +1,244 @@
+/*
+ * client.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: client.c,v 1.6 2004/02/26 21:57:55 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 "bthidd.h"
+#include "bthid_config.h"
+
+static int client_socket(bdaddr_p bdaddr, int 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 int connect_in_progress = 0;
+
+int
+client_rescan(bthid_server_p srv)
+{
+ static hid_device_p d = NULL;
+ bthid_session_p s = NULL;
+
+ 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->bdaddr)) == NULL) {
+ syslog(LOG_CRIT, "Could not open outbound session for %s. " \
+ "Not enough memory", 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
+ */
+
+int
+client_connect(bthid_server_p srv, int fd)
+{
+ bthid_session_p s = NULL;
+ hid_device_p d = NULL;
+ int error, 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;
+ 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, int psm)
+{
+ struct sockaddr_l2cap l2addr;
+ int 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;
+ memcpy(&l2addr.l2cap_bdaddr, NG_HCI_BDADDR_ANY, 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 = 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..5a7ab66
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/hid.c
@@ -0,0 +1,262 @@
+/*
+ * 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/26 21:47:35 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/consio.h>
+#include <sys/mouse.h>
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <usbhid.h>
+#include "bthidd.h"
+#include "bthid_config.h"
+
+#undef min
+#define min(x, y) (((x) < (y))? (x) : (y))
+
+/*
+ * Process data from control channel
+ */
+
+int
+hid_control(bthid_session_p s, char *data, int 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
+ */
+
+int
+hid_interrupt(bthid_session_p s, char *data, int len)
+{
+ hid_device_p hid_device = NULL;
+ hid_data_t d;
+ hid_item_t h;
+ int report_id, usage, page, val,
+ mouse_x, mouse_y, mouse_z, mouse_butt,
+ nkeys, keys[32]; /* XXX how big keys[] should be? */
+
+ assert(s != 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 ((unsigned char) 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 = nkeys = 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;
+ break;
+
+ case HUG_Y:
+ mouse_y = val;
+ break;
+
+ case HUG_WHEEL:
+ mouse_z = -val;
+ break;
+
+ case HUG_SYSTEM_SLEEP:
+ if (val)
+ syslog(LOG_NOTICE, "Sleep button pressed");
+ break;
+ }
+ break;
+
+ case HUP_KEYBOARD:
+ if (h.flags & HIO_VARIABLE) {
+ if (val && nkeys < sizeof(keys))
+ keys[nkeys ++] = usage;
+ } else {
+ if (val && nkeys < sizeof(keys))
+ keys[nkeys ++] = val;
+ data ++;
+ len --;
+
+ len = min(len, h.report_size);
+ while (len > 0) {
+ val = hid_get_data(data, &h);
+ if (val && nkeys < sizeof(keys))
+ keys[nkeys ++] = val;
+ data ++;
+ len --;
+ }
+ }
+ break;
+
+ case HUP_BUTTON:
+ mouse_butt |= (val << (usage - 1));
+ 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 mouse and keyboard events into kernel
+ * The code block below works, but it is not
+ * good enough
+ */
+
+ if (mouse_x != 0 || mouse_y != 0 || mouse_z != 0 || mouse_butt != 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/lexer.l b/usr.sbin/bluetooth/bthidd/lexer.l
new file mode 100644
index 0000000..93ad394
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/lexer.l
@@ -0,0 +1,100 @@
+%{
+/*
+ * lexer.l
+ *
+ * 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: lexer.l,v 1.2 2004/02/13 21:46:20 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <stdlib.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
+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 = NULL;
+
+ 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..f4fd560
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/parser.y
@@ -0,0 +1,447 @@
+%{
+/*
+ * parser.y
+ *
+ * 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: parser.y,v 1.3 2004/02/13 21:46:21 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <libusbhid.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.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);
+static int check_hid_device(hid_device_p hid_device);
+static void free_hid_device (hid_device_p hid_device);
+
+extern int yylineno;
+ char *config_file = BTHIDD_CONFFILE;
+ char *hids_file = BTHIDD_HIDSFILE;
+
+static char buffer[1024];
+static int hid_descriptor_size;
+static hid_device_t *hid_device = NULL;
+static LIST_HEAD(, hid_device) hid_devices;
+
+%}
+
+%union {
+ bdaddr_t bdaddr;
+ int 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(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 >= 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 */
+int
+read_config_file(void)
+{
+ extern FILE *yyin;
+ int 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 hid_device = LIST_FIRST(&hid_devices);
+
+ LIST_REMOVE(hid_device, next);
+ free_hid_device(hid_device);
+ }
+}
+
+/* Lookup config entry */
+hid_device_p
+get_hid_device(bdaddr_p bdaddr)
+{
+ hid_device_p hid_device;
+
+ LIST_FOREACH(hid_device, &hid_devices, next)
+ if (memcmp(&hid_device->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ return (hid_device);
+}
+
+/* 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 hid_device, 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 *) hid_device->desc;
+ int i;
+
+ fprintf(f,
+"device {\n" \
+" bdaddr %s;\n" \
+" control_psm 0x%x;\n" \
+" interrupt_psm 0x%d;\n" \
+" reconnect_initiate %s;\n" \
+" battery_power %s;\n" \
+" normally_connectable %s;\n" \
+" hid_descriptor {",
+ bt_ntoa(&hid_device->bdaddr, NULL),
+ hid_device->control_psm, hid_device->interrupt_psm,
+ hid_device->reconnect_initiate? "true" : "false",
+ hid_device->battery_power? "true" : "false",
+ hid_device->normally_connectable? "true" : "false");
+
+ for (i = 0; i < desc->size; i ++) {
+ if ((i % 8) == 0)
+ fprintf(stdout, "\n ");
+
+ fprintf(f, "0x%2.2x ", desc->data[i]);
+ }
+
+ fprintf(stdout,
+"\n" \
+" };\n" \
+"}\n");
+}
+
+/* Check config entry */
+static int
+check_hid_device(hid_device_p hid_device)
+{
+ if (get_hid_device(&hid_device->bdaddr) != NULL) {
+ SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
+ bt_ntoa(&hid_device->bdaddr, NULL));
+ return (0);
+ }
+
+ if (hid_device->control_psm == 0) {
+ SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
+ return (0);
+ }
+
+ if (hid_device->interrupt_psm == 0) {
+ SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
+ return (0);
+ }
+
+ if (hid_device->desc == NULL) {
+ SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
+ return (0);
+ }
+
+ return (1);
+}
+
+/* Free config entry */
+static void
+free_hid_device(hid_device_p hid_device)
+{
+ if (hid_device->desc != NULL)
+ hid_dispose_report_desc(hid_device->desc);
+
+ memset(hid_device, 0, sizeof(*hid_device));
+ free(hid_device);
+}
+
+/* Re-read hids file */
+int
+read_hids_file(void)
+{
+ FILE *f = NULL;
+ hid_device_t *hid_device = NULL;
+ char *line = NULL;
+ bdaddr_t bdaddr;
+ int 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 ((hid_device = get_hid_device(&bdaddr)) != NULL)
+ hid_device->new_device = 0;
+ }
+
+ fclose(f);
+
+ return (0);
+}
+
+/* Write hids file */
+int
+write_hids_file(void)
+{
+ char path[PATH_MAX];
+ FILE *f = NULL;
+ hid_device_t *hid_device = NULL;
+
+ 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(hid_device, &hid_devices, next)
+ if (!hid_device->new_device)
+ fprintf(f, "%s\n", bt_ntoa(&hid_device->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..d1887db
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/server.c
@@ -0,0 +1,316 @@
+/*
+ * 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.5 2004/02/26 21:43:36 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 "bthidd.h"
+#include "bthid_config.h"
+
+#undef max
+#define max(x, y) (((x) > (y))? (x) : (y))
+
+static int server_accept (bthid_server_p srv, int fd);
+static int server_process(bthid_server_p srv, int fd);
+
+/*
+ * Initialize server
+ */
+
+int
+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 = 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->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->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 = 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->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->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
+ */
+
+int
+server_do(bthid_server_p srv)
+{
+ struct timeval tv;
+ fd_set rfdset, wfdset;
+ int 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 int
+server_accept(bthid_server_p srv, int fd)
+{
+ bthid_session_p s = NULL;
+ hid_device_p d = NULL;
+ struct sockaddr_l2cap l2addr;
+ int len, new_fd;
+
+ 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);
+ }
+
+ /* Check if we have session for the device */
+ if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) {
+ /* 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);
+ }
+
+ d->new_device = 0; /* reset new device flag */
+ write_hids_file();
+
+ /* Create new inbound session */
+ if ((s = session_open(srv, &l2addr.l2cap_bdaddr)) == NULL) {
+ syslog(LOG_CRIT, "Could not open inbound session " \
+ "for %s. Not enough memory",
+ 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));
+
+ return (0);
+}
+
+/*
+ * Process data on the connection
+ */
+
+static int
+server_process(bthid_server_p srv, int fd)
+{
+ bthid_session_p s = session_by_fd(srv, fd);
+ char data[1024];
+ int len;
+
+ assert(s != NULL);
+
+ do {
+ len = read(fd, data, sizeof(data));
+ } 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);
+ }
+
+ if (fd == s->ctrl)
+ hid_control(s, data, len);
+ else
+ hid_interrupt(s, 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..523b918
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/session.c
@@ -0,0 +1,136 @@
+/*
+ * session.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: session.c,v 1.1 2004/02/12 22:46:59 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "bthidd.h"
+
+/*
+ * Create new session
+ */
+
+bthid_session_p
+session_open(bthid_server_p srv, bdaddr_p bdaddr)
+{
+ bthid_session_p s = NULL;
+
+ assert(srv != NULL);
+ assert(bdaddr != NULL);
+
+ if ((s = (bthid_session_p) malloc(sizeof(*s))) != NULL) {
+ s->srv = srv;
+ memcpy(&s->bdaddr, bdaddr, sizeof(s->bdaddr));
+ s->ctrl = s->intr = -1;
+ s->state = CLOSED;
+
+ 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 = NULL;
+
+ 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, int fd)
+{
+ bthid_session_p s = NULL;
+
+ assert(srv != NULL);
+ assert(fd >= 0);
+
+ LIST_FOREACH(s, &srv->sessions, next)
+ if (s->ctrl == fd || s->intr == 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 --;
+ }
+
+ memset(s, 0, sizeof(*s));
+ free(s);
+}
+
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..559b5f6
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.8
@@ -0,0 +1,176 @@
+.\" 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
+.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
+and attempts to send 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).
+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 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
+.El
+.Sh BUGS
+Most likely.
+Please report if found.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_hci 4 ,
+.Xr hcseriald 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.c b/usr.sbin/bluetooth/hccontrol/hccontrol.c
new file mode 100644
index 0000000..0bf5583
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.c
@@ -0,0 +1,274 @@
+/*
+ * 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/sysctl.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.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 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];
+ size_t size;
+
+ if (node == NULL)
+ usage();
+
+ 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 */
+
+/* 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 -n HCI_node_name [-h] 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..4424f76
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.h
@@ -0,0 +1,76 @@
+/*
+ * 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 */
+
+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 * const hci_link2str (int);
+char const * const hci_pin2str (int);
+char const * const hci_scan2str (int);
+char const * const hci_encrypt2str (int, int);
+char const * const hci_coding2str (int);
+char const * const hci_vdata2str (int);
+char const * const hci_hmode2str (int, char *, int);
+char const * const hci_ver2str (int);
+char const * const hci_manufacturer2str(int);
+char const * const hci_features2str (uint8_t *, char *, int);
+char const * const hci_cc2str (int);
+char const * const hci_con_state2str (int);
+char const * const hci_status2str (int);
+char const * 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..c8e03d2
--- /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[0], "%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[0], "%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 > 0xeff)
+ 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 [<bdaddr>]",
+"\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<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR",
+&hci_read_stored_link_key
+},
+{
+"write_stored_link_key <bdaddr> <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<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR\n" \
+"\t<key> - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx up to 16 bytes link key",
+&hci_write_stored_link_key
+},
+{
+"delete_stored_link_key [<bdaddr>]",
+"\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<bdaddr> - xx:xx:xx:xx:xx:xx BD_ADDR",
+&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..12b685b
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/info.c
@@ -0,0 +1,215 @@
+/*
+ * 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: %#02x\n", 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..c8e5cb1
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/link_control.c
@@ -0,0 +1,966 @@
+/*
+ * 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;
+ uint8_t 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:
+ /* 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);
+
+ /* inquiry length (N * 1.28) sec, range 0x01 - 0x30 */
+ case 2:
+ if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x1 || n0 > 0x30)
+ return (USAGE);
+
+ cp.inquiry_length = (n0 & 0xff);
+
+ /* number of responses, range 0x00 - 0xff */
+ case 1:
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 > 0xff)
+ return (USAGE);
+
+ cp.num_responses = (n0 & 0xff);
+
+ /* use defaults */
+ case 0:
+ 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)
+{
+ struct inquiry_response {
+ bdaddr_t bdaddr;
+ uint8_t page_scan_rep_mode;
+ uint8_t page_scan_period_mode;
+ uint8_t page_scan_mode;
+ uint8_t class[NG_HCI_CLASS_SIZE];
+ uint16_t clock_offset;
+ } *ir = (struct 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->class[2], ir->class[1], ir->class[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[2], "%d", &n0) != 1)
+ return (USAGE);
+
+ cp.accept_role_switch = n0 ? 1 : 0;
+
+ case 5:
+ /* clock offset */
+ if (sscanf(argv[2], "%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[2], "%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[0], "%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: %#02x\n", 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> - remote unit address\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 <bdaddr> <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<bdaddr> - xx:xx:xx:xx:xx:xx remote unit BD_ADDR\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..e00bac5
--- /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 <bdaddr> <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<bdaddr> - xx:xx:xx:xx:xx:xx; device bdaddr\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..863d516
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/node.c
@@ -0,0 +1,573 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.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 */
+
+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
+},
+{
+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..d4c1687
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/util.c
@@ -0,0 +1,378 @@
+/*
+ * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * const
+hci_ver2str(int ver)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "v1.0B",
+ /* 0x01 */ "v1.1"
+ };
+
+ return (ver >= SIZE(t)? "?" : t[ver]);
+} /* hci_ver2str */
+
+char const * const
+hci_manufacturer2str(int m)
+{
+ static char const * const t[] = {
+ /* 0000 */ "Ericsson Mobile Communications",
+ /* 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 */ "Telencomm 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."
+ };
+
+ return (m >= SIZE(t)? "?" : t[m]);
+} /* hci_manufacturer2str */
+
+char const * 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 * 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 * 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 * 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 * 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..d9a106f
--- /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?= 1
+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..b7c2322
--- /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 raw HCI socket and listens for the
+.Dv Link_Key_Request ,
+.Dv PIN_Code_Request
+and
+.Dv Link_Key_Notification
+HCI events.
+.Pp
+Once
+.Dv Link_Key_Request
+or
+.Dv PIN_Code_Request
+HCI event is received, the daemon will
+scan configuration file for 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, link key takes precedence over PIN code.
+If link key was not specified, it means device must generate link key from
+PIN code.
+If entry was found and the link key (or PIN code) exists then 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
+event and caches link keys created from the PIN codes in the 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 exists, the link keys file gets processed by
+.Nm
+daemon after it processes its main configuration file.
+The link keys file gets written every time
+.Nm
+daemon is gracefully shutdown.
+It is possible to force
+.Nm
+daemon to re-read its main configuration file and dump link keys file by sending
+.Dv HUP
+signal to the
+.Nm
+process.
+User is not expected to modify 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 name of the configuration file.
+The default is
+.Pa /etc/bluetooth/hcsecd.conf .
+.It Fl h
+Display usage message and exit.
+.El
+.Sh BUGS
+Currently there is no way to select link key or PIN code based on which local
+device received the request.
+Everything is based on remote device BD_ADDR.
+Also might implement interface for external helpers to obtain link keys and
+PIN codes.
+.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
diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.c b/usr.sbin/bluetooth/hcsecd/hcsecd.c
new file mode 100644
index 0000000..a1b72a3
--- /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, 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)
+ if (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(cp->pin, pin, sizeof(cp->pin));
+ cp->pin_size = strlen(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..d2e3696
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/parser.y
@@ -0,0 +1,432 @@
+%{
+/*
+ * 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 "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 BDADDR " \
+ "'%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 = (u_int8_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 = (u_int8_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..f21331b
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.5 2003/08/14 20:06:21 max Exp $
+# $FreeBSD$
+
+PROG= hcseriald
+MAN= hcseriald.8
+SRCS= hcseriald.c
+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..2bf9c54
--- /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/cuaa0 .
+.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..42231b4
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/hcseriald.c
@@ -0,0 +1,281 @@
+/*
+ * 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) {
+ pid_t pid = fork();
+
+ if (pid == (pid_t) -1) {
+ syslog(LOG_ERR, "Could not fork(). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (pid != 0)
+ exit(0);
+
+ if (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/cuaa1\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..e98f301
--- /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 DIAGNOSTICS
+.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..e62d2af
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.6 2003/08/14 20:06:24 max Exp $
+# $FreeBSD$
+
+PROG= l2ping
+MAN= l2ping.8
+SRCS= l2ping.c
+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..a990e71
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/l2ping.8
@@ -0,0 +1,101 @@
+.\" 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 fh
+.Fl a Ar BD_ADDR
+.Op Fl c Ar count
+.Op Fl i Ar delay
+.Op Fl S Ar BD_ADDR
+.Op Fl s Ar size
+.Sh DESCRIPTION
+The
+.Nm
+utility uses L2CAP
+.Dv ECHO_REQUEST
+datagram to elicit an L2CAP
+.Dv ECHO_RESPONSE
+from a remote device.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar BD_ADDR
+Address of remote device to ping.
+Example:
+.Fl a Li 00:01:02:03:04:05 .
+.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 S Ar BD_ADDR
+Send L2CAP
+.Dv ECHO_REQUEST
+from local device that has
+.Ar BD_ADDR .
+Example:
+.Fl S Li 00:05:04:03:02:01 .
+.It Fl s Ar size
+Specify the number of payload bytes to be sent.
+The default is the minimum L2CAP MTU minus the size of the L2CAP signalling
+command header (44 bytes).
+The maximum size is 65531.
+Use this option with caution.
+Some implementations may not like large sizes and may hang or even crash.
+.El
+.Sh BUGS
+Could collect more statistic.
+Could check for duplicated, corrupted and lost packets.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_l2cap 4 ,
+.Xr l2control 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/l2ping/l2ping.c b/usr.sbin/bluetooth/l2ping/l2ping.c
new file mode 100644
index 0000000..56d8ff6
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/l2ping.c
@@ -0,0 +1,275 @@
+/*
+ * 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;
+
+ /* 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;
+
+ /* Parse command line arguments */
+ while ((n = getopt(argc, argv, "a:c:fi:n:s: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 '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 '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 's':
+ echo_size = atoi(optarg);
+ if (echo_size < sizeof(int32_t))
+ usage();
+
+ if (echo_size > NG_L2CAP_MAX_ECHO_SIZE)
+ echo_size = NG_L2CAP_MAX_ECHO_SIZE;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (memcmp(&dst, NG_HCI_BDADDR_ANY, sizeof(dst)) == 0)
+ usage();
+
+ 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,
+ bt_ntoa(&dst, NULL),
+ 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(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 -s size -h]\n");
+ fprintf(stderr, "Where:\n");
+ fprintf(stderr, "\t-S bd_addr - Source BD_ADDR\n");
+ fprintf(stderr, "\t-a bd_addr - Remote BD_ADDR to ping\n");
+ fprintf(stderr, "\t-c count - Number of packets to send\n");
+ fprintf(stderr, "\t-f - No delay (soft of flood)\n");
+ fprintf(stderr, "\t-i wait - Delay between packets (sec)\n");
+ fprintf(stderr, "\t-s size - Packet size (bytes), " \
+ "between %zd and %zd\n", sizeof(int32_t), NG_L2CAP_MAX_ECHO_SIZE);
+ fprintf(stderr, "\t-h - Display this message\n");
+
+ 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..2369242
--- /dev/null
+++ b/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.8
@@ -0,0 +1,303 @@
+.\" 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 BD_ADDR
+.Fl C Ar channel
+.Fl l Ar label
+.Fl u Ar N
+.Nm
+.Fl s
+.Op Fl dh
+.Op Fl a Ar BD_ADDR
+.Fl C Ar channel
+.Fl l Ar label
+.Sh DESCRIPTION
+The
+.Nm
+daemon is a simple wrapper daemon that allows to use standard
+.Xr ppp 8
+on RFCOMM connection.
+It can operate in two modes: client and server.
+.Pp
+In the client mode,
+.Nm
+opens an RFCOMM connection to the specified server's
+.Ar BD_ADRR
+and
+.Ar channel .
+Once RFCOMM connection is established,
+.Nm
+executes
+.Xr ppp 8
+in
+.Fl direct
+mode with the specified
+.Ar label .
+The
+.Xr ppp 8
+in its turn operates over the RFCOMM connection just like it would operate
+over the standard serial port thus allowing user to
+.Dq "dial out"
+and connect to the Internet.
+.Pp
+In the server mode,
+.Nm
+opens an RFCOMM socket and listens for incomming connections from remote clients.
+Once the new incomming connection is accepted,
+.Nm
+forks and executes
+.Xr ppp 8
+in
+.Fl direct
+mode with the specified
+.Ar label .
+The
+.Xr ppp 8
+in its turn operates over the RFCOMM connection just like it would operate over
+the standard serial port thus providing network connectivity to remote clients.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar BD_ADDR
+In the client mode, this required option specifies the remote BD_ADDR of the
+RFCOMM server.
+In the server mode, this option can be used to specify the local
+BD_ADDR to listen on.
+By default, server will listen on
+.Dv ANY
+address.
+.It Fl C Ar channel
+In both client and server modes, this required option specifies RFCOMM channel
+to connect to or listen on.
+In the server mode, RFCOMM channel should be a number between 1 and 30.
+In the client mode, RFCOMM channel could either be a number between 1 and 30
+or a service name.
+Supported service names are:
+.Cm DUN
+(for DialUp Networking service) and
+.Cm LAN
+(for LAN Access Using PPP service).
+If service name was specified instead of numeric RFCOMM channel then
+.Nm
+utility will try to obtain RFCOMM channel for the service via 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 h
+Display usage message and exit.
+.It Fl l Ar label
+In both client and server modes, this required option specifies which
+.Xr ppp 8
+label will be used.
+.It Fl s
+Act as an RFCOMM server.
+.It Fl u Ar N
+This option maps directly onto
+.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 the 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, RFCOMM connection
+is used as a null-modem connection between client and server.
+Both client
+and server will start talking PPP right after RFCOMM connection was 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
+ # 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 LAN and DUN (Dial-Up Networking) access.
+The client's configuration for the LAN access is very similar to 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 the DUN access is different.
+In this scenario, the client gets connected to the virtual serial port on the
+server.
+To open a PPP session, 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.
+The example of such 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 \\\\dATD\\\\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 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 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 incomming connection has been accepted,
+.Nm
+will execute
+.Xr ppp 8
+in
+.Fl direct
+mode with
+.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, the
+.Nm
+will execute
+.Xr ppp 8
+in
+.Fl direct
+mode with
+.Dq Li rfcomm-client
+label.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh CAVEAT
+The
+.Nm
+utility in server mode will try to register Bluetooth LAN Access Over PPP
+service with local SPD daemon.
+If local SDP daemon is not running the
+.Nm
+utility will exit with error.
+.Sh SEE ALSO
+.Xr rfcomm_sppd 1 ,
+.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..892cf36
--- /dev/null
+++ b/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c
@@ -0,0 +1,407 @@
+/*
+ * rfcomm_pppd.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: 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;
+ pid_t pid;
+
+ memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
+ channel = 0;
+ detach = 1;
+ server = 0;
+ service = 0;
+
+ /* Parse command line arguments */
+ while ((s = getopt(argc, argv, "a:cC:dhl:su:")) != -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 'l': /* PPP label */
+ label = optarg;
+ break;
+
+ case 's': /* server */
+ server = 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) {
+ pid = fork();
+ if (pid == (pid_t) -1) {
+ syslog(LOG_ERR, "Could not fork(). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (pid != 0)
+ exit(0);
+
+ if (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);
+ }
+
+ for (done = 0; !done; ) {
+ int 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 bdaddr BDADDR 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-l label Use PPP label (required)\n" \
+"\t-s Act as a server\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..1478aee
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.8
@@ -0,0 +1,114 @@
+.\" 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 spdcontrol
+.Nd SDP query utility
+.Sh SYNOPSIS
+.Nm
+.Fl h
+.Nm
+.Fl a Ar BD_ADDR
+.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 specified Service Discovery Protocol (SDP) server.
+Remote SDP servers are identified by their BD_ADDRs.
+Connection to the local SDP server is made via control socket.
+The
+.Nm
+utility will use Service Search Attribute Request and 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 remote device with the specified BD_ADDR.
+Example:
+.Fl a Li 00:01:02:03:04:05 .
+.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).
+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 Browse
+.It Cm Search
+.El
+.Sh CAVEAT
+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 DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.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..6cb4863
--- /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 bdaddr specify bdaddr\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..dad6563
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/search.c
@@ -0,0 +1,707 @@
+/*
+ * 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 <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_GET128(&uuid, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ *(uint32_t *)&uuid.b[0],
+ *(uint16_t *)&uuid.b[4],
+ *(uint16_t *)&uuid.b[6],
+ *(uint16_t *)&uuid.b[8],
+ *(uint16_t *)&uuid.b[10],
+ *(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_GET128(&value.int128, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ *(uint32_t *)&value.int128.b[0],
+ *(uint16_t *)&value.int128.b[4],
+ *(uint16_t *)&value.int128.b[6],
+ *(uint16_t *)&value.int128.b[8],
+ *(uint16_t *)&value.int128.b[10],
+ *(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:
+ case SDP_DATA_UUID128:
+ SDP_GET128(&value.int128, start);
+ fprintf(stdout, "u/int/uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ *(uint32_t *)&value.int128.b[0],
+ *(uint16_t *)&value.int128.b[4],
+ *(uint16_t *)&value.int128.b[6],
+ *(uint16_t *)&value.int128.b[8],
+ *(uint16_t *)&value.int128.b[10],
+ *(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_GET128(&uuid, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ",
+ *(uint32_t *)&uuid.b[0],
+ *(uint16_t *)&uuid.b[4],
+ *(uint16_t *)&uuid.b[6],
+ *(uint16_t *)&uuid.b[8],
+ *(uint16_t *)&uuid.b[10],
+ *(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);
+ }
+
+ if (service < SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER)
+ 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..2b8b495
--- /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 irmc.c irmc_command.c lan.c log.c main.c \
+ opush.c profile.c provider.c sar.c scr.c sd.c server.c sp.c \
+ srr.c ssar.c ssr.c sur.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/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..a5612ed
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/lan.c
@@ -0,0 +1,177 @@
+/*
+ * 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;
+
+ if (buf + 2 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UINT8, buf);
+ SDP_PUT8(lan->load_factor, buf);
+
+ return (2);
+}
+
+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/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/profile.c b/usr.sbin/bluetooth/sdpd/profile.c
new file mode 100644
index 0000000..d49929f
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/profile.c
@@ -0,0 +1,382 @@
+/*
+ * 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;
+
+ 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
+ };
+
+ 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);
+ }
+
+ 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);
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(*((uint16_t const *)data)++, buf);
+ }
+
+ 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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/profile.h b/usr.sbin/bluetooth/sdpd/profile.h
new file mode 100644
index 0000000..0ca4221
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/profile.h
@@ -0,0 +1,90 @@
+/*
+ * 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 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_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..f6f482d
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/scr.c
@@ -0,0 +1,91 @@
+/*
+ * 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 || 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..52f6c95
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sd.c
@@ -0,0 +1,212 @@
+/*
+ * 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_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 + 13 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(11, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(9, buf);
+
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf);
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(NG_L2CAP_PSM_SDP, buf);
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(1, buf); /* version */
+
+ return (13);
+}
+
+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_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..3bd46c8
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sdpd.8
@@ -0,0 +1,136 @@
+.\" 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 Sevice
+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 assigment should be done at service registration time.
+.Pp
+Access rights on the control socket define which application can register,
+remove or change the service.
+The application must be able to write to and read from the control socket
+in order to perform any query to the Service Database via control socket.
+.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 BUGS
+Most likely.
+Please report if found.
+.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
diff --git a/usr.sbin/bluetooth/sdpd/server.c b/usr.sbin/bluetooth/sdpd/server.c
new file mode 100644
index 0000000..14b2041
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/server.c
@@ -0,0 +1,547 @@
+/*
+ * 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/select.h>
+#include <sys/queue.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.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, 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 (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 = 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].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].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, size;
+ uint16_t omtu;
+
+ 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);
+
+ 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 {
+ 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].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 (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);
+
+ 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..6f8f088
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/server.h
@@ -0,0 +1,101 @@
+/*
+ * 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 reserved : 2;
+ 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..a1f3ded
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/srr.c
@@ -0,0 +1,138 @@
+/*
+ * 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 || 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..d6f5095
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/ssar.c
@@ -0,0 +1,225 @@
+/*
+ * 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"
+
+/* 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, uuid;
+
+ /*
+ * 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);
+
+ SDP_GET16(uuid, sspptr);
+ ssplen -= 2;
+ break;
+
+ case SDP_DATA_UUID32: /* XXX FIXME */
+ case SDP_DATA_UUID128: /* XXX FIXME */
+ 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;
+
+ if (provider->profile->uuid != uuid &&
+ SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP != uuid)
+ 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..2ba6790
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/ssr.c
@@ -0,0 +1,255 @@
+/*
+ * 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"
+
+/*
+ * 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, uuid;
+
+ /*
+ * 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);
+
+ SDP_GET16(uuid, req);
+ ssplen -= 2;
+ break;
+
+ case SDP_DATA_UUID32: /* XXX FIXME */
+ case SDP_DATA_UUID128: /* XXX FIXME */
+ 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;
+
+ if (provider->profile->uuid == uuid ||
+ SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP == uuid) {
+ 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..6d7f778
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sur.c
@@ -0,0 +1,82 @@
+/*
+ * 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 || 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/boot0cfg/Makefile b/usr.sbin/boot0cfg/Makefile
new file mode 100644
index 0000000..3f87438
--- /dev/null
+++ b/usr.sbin/boot0cfg/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= boot0cfg
+MAN= boot0cfg.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/boot0cfg/boot0cfg.8 b/usr.sbin/boot0cfg/boot0cfg.8
new file mode 100644
index 0000000..ff21a85
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.8
@@ -0,0 +1,182 @@
+.\" 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 February 21, 1999
+.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 f Ar file
+.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 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 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 nopacket .
+.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 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 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"
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.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 MBR to be written
+to the wrong disk.
+Be careful!
diff --git a/usr.sbin/boot0cfg/boot0cfg.c b/usr.sbin/boot0cfg/boot0cfg.c
new file mode 100644
index 0000000..21f0ce7
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.c
@@ -0,0 +1,449 @@
+/*
+ * 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 <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 */
+#define OFF_OPT 0x1b9 /* offset: default boot option */
+#define OFF_DRIVE 0x1ba /* offset: setdrv drive */
+#define OFF_FLAGS 0x1bb /* offset: option flags */
+#define OFF_TICKS 0x1bc /* offset: clock ticks */
+#define OFF_PTBL 0x1be /* offset: partition table */
+#define OFF_MAGIC 0x1fe /* offset: magic number */
+
+#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 char *mkrdev(const char *);
+static int argtoi(const char *, int, int, int);
+static void usage(void);
+
+/*
+ * 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, v_flag, o_flag;
+ int d_arg, m_arg, s_arg, t_arg;
+ int o_and, o_or;
+ 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:f: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 'f':
+ fpath = 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 = mkrdev(*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 */
+ 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);
+ } 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);
+
+ /* 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;
+}
+
+/*
+ * 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;
+ 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 (!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, p;
+ ssize_t n;
+ char *s;
+
+ 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 (flags != 0)
+ err(1, "%s", fname);
+#ifdef DIOCSMBR
+ for (p = 1; p < 5; p++) {
+ asprintf(&s, "%ss%d", fname, p);
+ fd = open(s, O_RDONLY);
+ if (fd < 0) {
+ free(s);
+ continue;
+ }
+ n = ioctl(fd, DIOCSMBR, (mbr));
+ if (n != 0)
+ err(1, "%s: ioctl DIOCSMBR", fname);
+ close(fd);
+ free(s);
+ return;
+ }
+#endif
+ err(1, "write_mbr: %s", fname);
+}
+
+/*
+ * 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\noptions=",
+ version >> 8, version & 0xff, mbr[OFF_DRIVE],
+ mbr[OFF_FLAGS] & 0xf, cv2(mbr + OFF_TICKS));
+ 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");
+ 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)
+{
+ static u_int8_t idold[] = {0xfe, 0x45, 0xf2, 0xe9, 0x00, 0x8a};
+
+ /* Check for old version, and return 0x100 if found. */
+ if (memcmp(bs + 0x1c, idold, sizeof(idold)) == 0)
+ return 0x100;
+
+ /* We have a newer boot0, so extract the version number and return it. */
+ return *(const int *)(bs + OFF_VERSION) & 0xffff;
+}
+
+/*
+ * 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)
+{
+ static u_int8_t id0[] = {0xfc, 0x31, 0xc0, 0x8e, 0xc0, 0x8e, 0xd8,
+ 0x8e, 0xd0, 0xbc, 0x00, 0x7c };
+ static u_int8_t id1[] = {'D', 'r', 'i', 'v', 'e', ' '};
+ static struct {
+ unsigned off;
+ unsigned len;
+ u_int8_t *key;
+ } ident[2] = {
+ {0x0, sizeof(id0), id0},
+ {0x1b2, sizeof(id1), id1}
+ };
+ unsigned int i;
+
+ for (i = 0; i < sizeof(ident) / sizeof(ident[0]); i++)
+ if (memcmp(bs + ident[i].off, ident[i].key, ident[i].len))
+ return 0;
+ return 1;
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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..e7ffd9d
--- /dev/null
+++ b/usr.sbin/boot98cfg/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= boot98cfg
+MAN= boot98cfg.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/boot98cfg/boot98cfg.8 b/usr.sbin/boot98cfg/boot98cfg.8
new file mode 100644
index 0000000..6d24b19
--- /dev/null
+++ b/usr.sbin/boot98cfg/boot98cfg.8
@@ -0,0 +1,99 @@
+.\" 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 SEE ALSO
+.Xr boot 8 ,
+.Xr fdisk 8
+.Sh DIAGNOSTICS
+.Ex -std
+.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..c4575c8
--- /dev/null
+++ b/usr.sbin/boot98cfg/boot98cfg.c
@@ -0,0 +1,295 @@
+/*
+ * 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$
+ */
+
+/*
+ * 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/param.h>
+#include <sys/stat.h>
+#include <sys/diskpc98.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>
+
+#define BOOTSIZE 0x2000
+#define IPLSIZE 512 /* IPL size */
+#define BOOTMENUSIZE 7168 /* Max HDD boot menu size */
+#define BOOTMENUOFF 0x400
+
+static char *mkrdev(char *);
+static void usage(void);
+static int read_boot(const char *, u_char *);
+static int write_boot(const char *, u_char *);
+
+u_char boot0buf[BOOTSIZE];
+u_char ipl[IPLSIZE];
+u_char menu[BOOTMENUSIZE];
+
+/*
+ * Produce a device path for a "canonical" name, where appropriate.
+ */
+static char *
+mkrdev(char *fname)
+{
+ char buf[MAXPATHLEN];
+ struct stat sb;
+ char *s;
+
+ s = (char *)fname;
+ if (!strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname);
+ if (stat(buf, &sb))
+ snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+ if (!(s = strdup(buf)))
+ err(1, NULL);
+ }
+ return s;
+}
+
+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);
+}
+
+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];
+
+ fd = open(disk, O_RDWR);
+ if (fd != -1) {
+ if (lseek(fd, 0, SEEK_SET) == -1)
+ err(1, "%s", disk);
+ if ((n = write(fd, boot, BOOTSIZE)) < 0)
+ err(1, "%s", disk);
+ if (n != BOOTSIZE)
+ errx(1, "%s: short write", disk);
+ close(fd);
+ return 0;
+ }
+
+ 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);
+}
+
+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 -s 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;
+}
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..11c6390
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/Makefile
@@ -0,0 +1,24 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+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.
+
+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..fabbe3c
--- /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 BUGS
+You may find the
+.Xr syslog 3
+loggings to be verbose.
+.Sh AUTHORS
+Written by
+.An Klas Heggemann Aq klas@nada.kth.se .
diff --git a/usr.sbin/bootparamd/bootparamd/bootparamd.c b/usr.sbin/bootparamd/bootparamd/bootparamd.c
new file mode 100644
index 0000000..159124e
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.c
@@ -0,0 +1,344 @@
+/*
+
+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 <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#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 unsigned long 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 __P((char *, char *, char *, int));
+int checkhost __P((char *, char *, int));
+
+bp_whoami_res *
+bootparamproc_whoami_1_svc(whoami, req)
+bp_whoami_arg *whoami;
+struct svc_req *req;
+{
+ long 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, 4);
+ }
+ 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;
+ static char *yp_domain;
+
+ 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 */
+ 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);
+ }
+ /* 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;
+ static char *yp_domain;
+
+/* 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 */
+ 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);
+ }
+ /* 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..4468232
--- /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;
+unsigned long route_addr = -1;
+struct sockaddr_in my_addr;
+char *bootpfile = "/etc/bootparams";
+
+extern void bootparamprog_1();
+static void usage __P((void));
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ SVCXPRT *transp;
+ struct hostent *he;
+ struct stat buf;
+ char 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..8db69ea
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/Makefile
@@ -0,0 +1,24 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= callbootd
+NOMAN= #true
+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 -l -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot_xdr.c: ${RPCSRC}
+ rpcgen -c -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot.h: ${RPCSRC}
+ rpcgen -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..5ed4608
--- /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 __P((void));
+int printgetfile __P((bp_getfile_res *));
+int printwhoami __P((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..4fed3e5
--- /dev/null
+++ b/usr.sbin/bsnmpd/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SUBDIR= gensnmptree \
+ bsnmpd
+
+.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..f172c66
--- /dev/null
+++ b/usr.sbin/bsnmpd/bsnmpd/Makefile
@@ -0,0 +1,43 @@
+# $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
+CLEANFILES= oid.h tree.c tree.h
+MAN= bsnmpd.1 snmpmod.3
+WARNS?= 6
+NO_WERROR=yes
+
+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.
+DPADD= ${LIBISC} ${LIBBSNMP}
+LDADD= -lisc -lbsnmp
+
+LDFLAGS= -export-dynamic
+
+oid.h: tree.def
+ gensnmptree -e ${XSYM} < ${.ALLSRC} > ${.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..dc95258
--- /dev/null
+++ b/usr.sbin/bsnmpd/gensnmptree/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+CONTRIB=${.CURDIR}/../../../contrib/bsnmp
+.PATH: ${CONTRIB}/gensnmptree
+
+PROG= gensnmptree
+MAN= gensnmptree.1
+CFLAGS+= -I${CONTRIB}/lib
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsnmpd/modules/Makefile b/usr.sbin/bsnmpd/modules/Makefile
new file mode 100644
index 0000000..ad5df5f
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/bsnmp/snmpd
+
+SUBDIR= snmp_mibII \
+ snmp_netgraph
+
+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..f4efa73
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/Makefile.inc
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+SHLIB_NAME= snmp_${MOD}.so.${SHLIB_MAJOR}
+SRCS+= ${MOD}_oid.h ${MOD}_tree.c ${MOD}_tree.h
+CLEANFILES+= ${MOD}_oid.h ${MOD}_tree.c ${MOD}_tree.h
+CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -I.
+
+${MOD}_oid.h: ${MOD}_tree.def
+ gensnmptree -e ${XSYM} < ${.ALLSRC} > ${.TARGET}
+
+.ORDER: ${MOD}_tree.c ${MOD}_tree.h
+${MOD}_tree.c ${MOD}_tree.h: ${MOD}_tree.def
+ gensnmptree -l -p ${MOD}_ < ${.ALLSRC}
+
+.if defined(DEFS)
+FILESGROUPS+= DEFS
+.endif
+DEFSDIR= ${SHAREDIR}/snmp/defs
+
+.if defined(BMIBS)
+FILESGROUPS+= BMIBS
+.endif
+BMIBSDIR= ${SHAREDIR}/snmp/mibs
+
+MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \
+ -e 's%@DEFPATH@%${DEFSDIR}/%g' \
+ -e 's%@MIBSPATH@%${BMIBSDIR}/%g'
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile b/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile
new file mode 100644
index 0000000..c677c53
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+CONTRIB=${.CURDIR}/../../../../contrib/bsnmp
+.PATH: ${CONTRIB}/snmp_mibII
+
+MOD= mibII
+SRCS= mibII.c mibII_ifmib.c mibII_ip.c mibII_interfaces.c \
+ mibII_ipaddr.c mibII_ifstack.c mibII_rcvaddr.c \
+ mibII_nettomedia.c mibII_tcp.c mibII_udp.c mibII_route.c
+XSYM= ipAddrTable ifTable ifRcvAddressEntry ifMIB ipMIB tcpMIB udpMIB \
+ ipForward ifIndex linkDown linkUp
+MAN= snmp_mibII.3
+
+DEFS= ${MOD}_tree.def
+INCS= snmp_${MOD}.h
+
+.include <bsd.lib.mk>
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..d032315
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+CONTRIB=${.CURDIR}/../../../../contrib/bsnmp
+
+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.lib.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..f470496
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3
@@ -0,0 +1,417 @@
+.\"
+.\" 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 performa,
+.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 reponse 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 asynchronuosly 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 registerred
+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
+.Fn
+.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 occures 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 occures 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 occurres 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
+who's 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.
+.Sh SEE ALSO
+.Xr snmpmod 3 ,
+.Xr gensnmptree 1
+.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..5aad23b
--- /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 "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 u_int32_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/btxld/Makefile b/usr.sbin/btxld/Makefile
new file mode 100644
index 0000000..912be22
--- /dev/null
+++ b/usr.sbin/btxld/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= btxld
+MAN= btxld.8
+SRCS= btxld.c elfh.c
+CFLAGS+=-I${.CURDIR}/../kgzip
+
+.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..95dff0c
--- /dev/null
+++ b/usr.sbin/btxld/btxld.8
@@ -0,0 +1,97 @@
+.\" 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 SEE ALSO
+.Xr ld 1 ,
+.Xr boot 8
+.Sh DIAGNOSTICS
+.Ex -std
+.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..9216031
--- /dev/null
+++ b/usr.sbin/btxld/btxld.c
@@ -0,0 +1,566 @@
+/*
+ * 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/stat.h>
+#include <sys/mman.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"
+#include "endian.h"
+#include "i386_a.out.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 (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 i386_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 ((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 i386_exec) && !I386_N_BADMAG(*ex)) {
+ hdr->fmt = fmt;
+ x = I386_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 i386_exec ex;
+ struct elfh eh;
+
+ switch (hdr->fmt) {
+ case F_AOUT:
+ memset(&ex, 0, sizeof(ex));
+ I386_N_SETMAGIC(ex, ZMAGIC, MID_ZERO, 0);
+ hdr->text = I386_N_ALIGN(ex, hdr->text);
+ ex.a_text = HTOLE32(hdr->text);
+ hdr->data = I386_N_ALIGN(ex, hdr->data);
+ ex.a_data = HTOLE32(hdr->data);
+ ex.a_entry = HTOLE32(hdr->entry);
+ writex(fd, &ex, sizeof(ex));
+ hdr->size = I386_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 (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..75b7e33
--- /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 <stddef.h>
+#include "elfh.h"
+#include "endian.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..6f03273
--- /dev/null
+++ b/usr.sbin/burncd/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= burncd
+MAN= burncd.8
+
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/burncd/burncd.8 b/usr.sbin/burncd/burncd.8
new file mode 100644
index 0000000..27ef698
--- /dev/null
+++ b/usr.sbin/burncd/burncd.8
@@ -0,0 +1,206 @@
+.\"
+.\" 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 October 18, 2002
+.Os
+.Dt BURNCD 8
+.Sh NAME
+.Nm burncd
+.Nd control the ATAPI CD-R/RW driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl delmnpqtv
+.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 media 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
+.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 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).
+Should be the last command to
+.Nm
+as the program exits when this has been done.
+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 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, is commonly used to create ISO9660 file system images
+from a given directory tree.
+.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 BUGS
+Probably, please report when found.
+.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 .
diff --git a/usr.sbin/burncd/burncd.c b/usr.sbin/burncd/burncd.c
new file mode 100644
index 0000000..500f070
--- /dev/null
+++ b/usr.sbin/burncd/burncd.c
@@ -0,0 +1,695 @@
+/*-
+ * 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 <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 global_fd_for_cleanup, quiet, verbose, saved_block_size, notracks;
+
+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 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);
+
+ for (arg = 0; arg < argc; arg++) {
+ if (!strcasecmp(argv[arg], "fixate")) {
+ fixate = 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
+ err(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)
+ err(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)
+ err(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)");
+ 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)
+ err(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]))
+ 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]))
+ 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)
+ err(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)
+ err(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) {
+ fprintf(stderr, "\nonly wrote %d of %jd bytes: %s\n",
+ res, (intmax_t)count, strerror(errno));
+ 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
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-delmnpqtv] [-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..dcf297d
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.1
@@ -0,0 +1,214 @@
+.\" $FreeBSD$
+.\"
+.Dd May 8, 2002
+.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 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 AUTHORS
+.An Jean-Marc Zucconi
+.An Andrey A. Chernov
+.An Serge V. Vakulenko
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1 .
diff --git a/usr.sbin/cdcontrol/cdcontrol.c b/usr.sbin/cdcontrol/cdcontrol.c
new file mode 100644
index 0000000..edebd65
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.c
@@ -0,0 +1,1262 @@
+/*
+ * 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.
+ */
+
+#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"
+
+#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> | 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 EOF:
+ 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;
+
+ 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 (! 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);
+
+ if (2 != sscanf (arg, "%d %d", &l, &r)) {
+ warnx("invalid command arguments");
+ return (0);
+ }
+
+ return setvol (l, r);
+
+ 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;
+
+ 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_EVENT, 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..e7a1606
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.8
@@ -0,0 +1,84 @@
+.\" 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 5, 1998
+.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.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+group database file
+.El
+.Sh SEE ALSO
+.Xr getgrent 3 ,
+.Xr group 5
+.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.
+Otherwise no
+output is produced.
+.Pp
+The
+.Nm
+utility returns
+.Dv EX_DATAERR
+if errors were found in the group file,
+and
+.Dv EX_OK
+otherwise.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
+.Sh BUGS
+Should check fields more thoroughly for allowed/disallowed
+characters, and 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..f448dae
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.c
@@ -0,0 +1,146 @@
+/*-
+ * 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 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 *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);
+ e++;
+ continue;
+ }
+
+ /* check if fourth field ended with a colon */
+ if (i < len) {
+ warnx("%s: line %d: too many fields", gfn, n);
+ e++;
+ continue;
+ }
+
+ /* 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);
+
+ /* 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++;
+ continue;
+ }
+
+#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);
+ 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..a0659b6
--- /dev/null
+++ b/usr.sbin/chown/chgrp.1
@@ -0,0 +1,145 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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 doesn't
+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 DIAGNOSTICS
+.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 FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+group ID file
+.El
+.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..c7718b6
--- /dev/null
+++ b/usr.sbin/chown/chown.8
@@ -0,0 +1,170 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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
+Don't 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 DIAGNOSTICS
+.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..7e28473
--- /dev/null
+++ b/usr.sbin/chown/chown.c
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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..342d849
--- /dev/null
+++ b/usr.sbin/chroot/chroot.8
@@ -0,0 +1,98 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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..16da462
--- /dev/null
+++ b/usr.sbin/chroot/chroot.c
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#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..5dc37b0
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.1
@@ -0,0 +1,101 @@
+.\" 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 "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 "message digest") and .inf (32-bit CRC) checksum
+formats are supported.
+.Pp
+The
+.Ar file
+operands may refer to regular files or to directories. Regular files
+named "md5", or which have an ".md5" or an ".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
+Options are as follows:
+.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 .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 .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 "md5" or "inf").
+.El
+.Sh SEE ALSO
+.Xr cksum 1 ,
+.Xr md5 1
+.Sh DIAGNOSTICS
+Exit status is 0 if no errors were detected, 1 if errors were found in
+a distribution, and 2 if usage errors, inaccessible input files, or
+other system errors were encountered.
+.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..92bcd0d
--- /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 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((char *)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 char *
+stripath(const char *path)
+{
+ const char *s;
+
+ return (char *)((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/config/Makefile b/usr.sbin/config/Makefile
new file mode 100644
index 0000000..6f6ecce
--- /dev/null
+++ b/usr.sbin/config/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= config
+MAN= config.8
+SRCS= config.y main.c lang.l mkmakefile.c mkheaders.c \
+ mkoptions.c y.tab.h
+
+WARNS?= 6
+CFLAGS+= -I. -I${.CURDIR}
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+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..63d7b32
--- /dev/null
+++ b/usr.sbin/config/config.5
@@ -0,0 +1,348 @@
+.\" 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 May 13, 2004
+.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
+.\" -------- CPU --------
+.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 --------
+.It Ic device Ar name Op Ar count
+Configures device
+.Ar name
+for inclusion into the kernel image.
+If
+.Ar count
+is specified, the device is configured for
+.Ar count
+instances.
+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 --------
+.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 --------
+.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 --------
+.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 .
+.\" -------- IDENT --------
+.It Ic ident Ar name
+Set the kernel name to
+.Ar name .
+At least one
+.Ic ident
+directive is required.
+.\" -------- INCLUDE --------
+.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 --------
+.It Ic machine Ar arch
+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 amd64
+The AMD x86-64 architecture.
+.It Cm i386
+The Intel x86 based PC architecture.
+.It Cm ia64
+The Intel IA64 architecture.
+.It Cm pc98
+The PC98 architecture.
+.It Cm powerpc
+The IBM PowerPC architecture.
+.It Cm sparc64
+The Sun Sparc64 architecture.
+.El
+.Pp
+A kernel configuration file may have only one
+.Ic machine
+directive.
+.\" -------- MAKEOPTION --------
+.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
+.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="foobar"
+makeoptions MYNULLMAKEOPTION
+.Ed
+.\" -------- MAXUSERS --------
+.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 .
+.\" -------- NOMAKEOPTION --------
+.It Ic nomakeoption 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 --------
+.It Ic nooption Ar kerneloptionname
+Remove kernel option
+.Ar kerneloptionname
+from the list of previously defined options.
+This directive can be used to cancel the effects of
+.Ic options
+directives in files included using
+.Ic include .
+.\" -------- OPTIONS --------
+.It Ic options Ar optionspecs
+Add compile time kernel options to the kernel build.
+The argument
+.Ar optionspecs
+is a comma separated list of option specifications.
+Each option specification has the form
+.Pp
+.D1 Ar KernelOptionName Ns Op = Ns Ar OptionValue
+.Pp
+If
+.Ar OptionValue
+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 --------
+.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 --------
+.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..c215793
--- /dev/null
+++ b/usr.sbin/config/config.8
@@ -0,0 +1,261 @@
+.\" 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.
+.\"
+.\" @(#)config.8 8.2 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\"
+.Dd July 4, 2001
+.Dt CONFIG 8
+.Os
+.Sh NAME
+.Nm config
+.Nd build system configuration files
+.Sh SYNOPSIS
+.Nm
+.Op Fl gp
+.Op Fl d Ar destdir
+.Ar SYSTEM_NAME
+.Sh DESCRIPTION
+This is the old version of the
+.Nm
+utility.
+It understands the old autoconfiguration scheme
+used on the HP300, i386, DECstation, and derivative platforms.
+The new version of
+.Nm
+is used with the
+SPARC platform.
+Only the version of
+.Nm
+applicable to the architecture that you are running
+will be installed on your machine.
+.Pp
+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 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 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 ioconf.c ,
+a description
+of what I/O devices are attached to the system;
+.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.
+.Pp
+If the
+.Cd "options INCLUDE_CONFIG_FILE"
+is used in the configuration file the
+entire input file is embedded in the new kernel.
+This means that
+.Xr strings 1
+can be used to extract it from a kernel:
+to extract the configuration information, use the command
+.Pp
+.Dl "strings -n 3 kernel | sed -n 's/^___//p'"
+.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 BUGS
+The line numbers reported in error messages are usually off by one.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.1 .
diff --git a/usr.sbin/config/config.h b/usr.sbin/config/config.h
new file mode 100644
index 0000000..cbeda79
--- /dev/null
+++ b/usr.sbin/config/config.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+/*
+ * Config.
+ */
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct file_list {
+ STAILQ_ENTRY(file_list) f_next;
+ char *f_fn; /* the name */
+ int f_type; /* type or count */
+ 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_needs;
+ char *f_warn; /* warning message */
+};
+
+struct files_name {
+ char *f_name;
+ STAILQ_ENTRY(files_name) f_next;
+};
+
+/*
+ * Types.
+ */
+#define NORMAL 1
+#define INVISIBLE 2
+#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 NEED_COUNT 8
+#define ISDUP 16
+#define NOWERROR 32
+
+struct device {
+ int d_done; /* processed */
+ char *d_name; /* name of device (e.g. rk11) */
+ int d_count; /* device count */
+#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.
+ */
+char *machinename;
+
+/*
+ * 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_HEAD(opt_head, opt) opt, mkopt;
+
+struct opt_list {
+ char *o_name;
+ char *o_file;
+ SLIST_ENTRY(opt_list) o_next;
+};
+
+SLIST_HEAD(, opt_list) otab;
+
+extern char *ident;
+extern char *env;
+extern char *hints;
+extern int do_trace;
+extern int envmode;
+extern int hintmode;
+
+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 headers(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 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..d16982a
--- /dev/null
+++ b/usr.sbin/config/config.y
@@ -0,0 +1,364 @@
+%union {
+ char *str;
+ int val;
+ struct file_list *file;
+}
+
+%token ARCH
+%token COMMA
+%token CONFIG
+%token CPU
+%token DEVICE
+%token NODEVICE
+%token ENV
+%token EQUALS
+%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
+
+%{
+
+/*
+ * 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 <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+
+struct device_head dtab;
+char *ident;
+char *env;
+int envmode;
+char *hints;
+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);
+
+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 ID SEMICOLON
+ = { include($2, 0); };
+ |
+ FILES ID SEMICOLON
+ = { newfile($2); };
+ |
+ SEMICOLON
+ |
+ error SEMICOLON
+ ;
+
+Config_spec:
+ ARCH Save_id
+ = {
+ if (machinename != NULL)
+ errx(1, "%s:%d: only one machine directive is allowed",
+ yyfile, yyline);
+ machinename = $2;
+ } |
+ CPU Save_id
+ = {
+ struct cputype *cp =
+ (struct cputype *)malloc(sizeof (struct cputype));
+ memset(cp, 0, sizeof(*cp));
+ cp->cpu_name = $2;
+ SLIST_INSERT_HEAD(&cputype, cp, cpu_next);
+ } |
+ OPTIONS Opt_list
+ |
+ NOOPTION Save_id
+ = { rmopt(&opt, $2); } |
+ MAKEOPTIONS Mkopt_list
+ |
+ NOMAKEOPTION Save_id
+ = { rmopt(&mkopt, $2); } |
+ IDENT ID
+ = { ident = $2; } |
+ System_spec
+ |
+ MAXUSERS NUMBER
+ = { maxusers = $2; } |
+ PROFILE NUMBER
+ = { profiling = $2; } |
+ ENV ID
+ = {
+ env = $2;
+ envmode = 1;
+ } |
+ HINTS ID
+ = {
+ hints = $2;
+ 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); };
+
+System_parameter_list:
+ System_parameter_list ID
+ | ID
+ ;
+
+Opt_list:
+ Opt_list COMMA Option
+ |
+ Option
+ ;
+
+Option:
+ Save_id
+ = {
+ char *s;
+
+ newopt(&opt, $1, NULL);
+ if ((s = strchr($1, '=')))
+ errx(1, "%s:%d: The `=' in options should not be "
+ "quoted", yyfile, yyline);
+ } |
+ Save_id EQUALS Opt_value
+ = {
+ newopt(&opt, $1, $3);
+ } ;
+
+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("")); } |
+ Save_id EQUALS Opt_value
+ = { newopt(&mkopt, $1, $3); } ;
+
+Dev:
+ ID
+ = { $$ = $1; }
+ ;
+
+Device_spec:
+ DEVICE Dev
+ = {
+ newopt(&opt, devopt($2), ns("1"));
+ /* and the device part */
+ newdev($2, UNKNOWN);
+ } |
+ DEVICE Dev NUMBER
+ = {
+ newopt(&opt, devopt($2), ns("1"));
+ /* and the device part */
+ newdev($2, $3);
+ if ($3 == 0)
+ errx(1, "%s:%d: devices with zero units are not "
+ "likely to be correct", yyfile, yyline);
+ } |
+ NODEVICE Dev
+ = {
+ char *s = devopt($2);
+
+ rmopt(&opt, s);
+ free(s);
+ /* and the device part */
+ rmdev($2);
+ } ;
+
+%%
+
+void
+yyerror(const char *s)
+{
+
+ errx(1, "%s:%d: %s", yyfile, yyline + 1, s);
+}
+
+/*
+ * Add a new file to the list of files.
+ */
+static void
+newfile(char *name)
+{
+ struct files_name *nl;
+
+ nl = (struct files_name *) malloc(sizeof *nl);
+ bzero(nl, sizeof *nl);
+ nl->f_name = name;
+ STAILQ_INSERT_TAIL(&fntab, nl, f_next);
+}
+
+/*
+ * add a device to the list of devices
+ */
+static void
+newdev(char *name, int count)
+{
+ struct device *np;
+
+ np = (struct device *) malloc(sizeof *np);
+ memset(np, 0, sizeof(*np));
+ np->d_name = name;
+ np->d_count = count;
+ STAILQ_INSERT_TAIL(&dtab, np, d_next);
+}
+
+/*
+ * remove a device from the list of devices
+ */
+static void
+rmdev(char *name)
+{
+ struct device *dp, *rmdp;
+
+ STAILQ_FOREACH(dp, &dtab, d_next) {
+ if (eq(dp->d_name, name)) {
+ rmdp = dp;
+ dp = STAILQ_NEXT(dp, d_next);
+ STAILQ_REMOVE(&dtab, rmdp, device, d_next);
+ free(rmdp->d_name);
+ free(rmdp);
+ if (dp == NULL)
+ break;
+ }
+ }
+}
+
+static void
+newopt(struct opt_head *list, char *name, char *value)
+{
+ struct opt *op;
+
+ op = (struct opt *)malloc(sizeof (struct opt));
+ memset(op, 0, sizeof(*op));
+ op->op_name = name;
+ op->op_ownfile = 0;
+ op->op_value = value;
+ SLIST_INSERT_HEAD(list, op, op_next);
+}
+
+static void
+rmopt(struct opt_head *list, char *name)
+{
+ struct opt *op, *rmop;
+
+ SLIST_FOREACH(op, list, op_next) {
+ if (eq(op->op_name, name)) {
+ rmop = op;
+ op = SLIST_NEXT(op, op_next);
+ SLIST_REMOVE(list, rmop, opt, op_next);
+ free(rmop->op_name);
+ if (rmop->op_value != NULL)
+ free(rmop->op_value);
+ free(rmop);
+ if (op == NULL)
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/config/configvers.h b/usr.sbin/config/configvers.h
new file mode 100644
index 0000000..4e6ccb9
--- /dev/null
+++ b/usr.sbin/config/configvers.h
@@ -0,0 +1,11 @@
+/*
+ * 6 digits of version. The most significant are branch indicators
+ * (eg: RELENG_2_2 = 22, -current presently = 50 etc). The least
+ * significant digits are incremented for each incompatible change.
+ *
+ * The numbering scheme is inspired by the sys/conf/newvers.sh RELDATE
+ * and <osreldate.h> system.
+ *
+ * $FreeBSD$
+ */
+#define CONFIGVERS 500013
diff --git a/usr.sbin/config/lang.l b/usr.sbin/config/lang.l
new file mode 100644
index 0000000..7984bf9
--- /dev/null
+++ b/usr.sbin/config/lang.l
@@ -0,0 +1,278 @@
+%{
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)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 },
+ { "device", DEVICE },
+ { "nodevice", NODEVICE },
+ { "env", ENV },
+ { "hints", HINTS },
+ { "ident", IDENT },
+ { "machine", ARCH }, /* MACHINE is defined in /sys/param.h */
+ { "makeoptions", MAKEOPTIONS },
+ { "nomakeoption", NOMAKEOPTION },
+ { "maxusers", MAXUSERS },
+ { "profile", PROFILE },
+ { "option", OPTIONS },
+ { "options", OPTIONS },
+ { "nooption", 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 *);
+
+%}
+WORD [A-Za-z_][-A-Za-z_]*
+ID [A-Za-z_][-A-Za-z_0-9]*
+%START NONUM TOEOL
+%%
+<NONUM>{WORD} {
+ int i;
+
+ BEGIN 0;
+ if ((i = kw_lookup(yytext)) == -1)
+ {
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+ return i;
+ }
+<INITIAL>{WORD}/[0-9]* {
+ int i;
+
+ if ((i = kw_lookup(yytext)) == -1)
+ REJECT;
+ if (i == DEVICE || i == NODEVICE)
+ BEGIN NONUM;
+ return i;
+ }
+<INITIAL>{ID} {
+ BEGIN 0;
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+\\\"[^"]+\\\" {
+ 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; }
+<<EOF>> {
+ int tok;
+
+ if (inclp == NULL)
+ return YY_NULL;
+ tok = endinclude();
+ if (tok != 0)
+ return tok;
+ /* otherwise continue scanning */
+ }
+. { 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;
+}
+
+
+/*
+ * 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;
+
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ yyerror("cannot open file");
+ return (-1);
+ }
+ 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..a22d0ad
--- /dev/null
+++ b/usr.sbin/config/main.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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) 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/file.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "y.tab.h"
+#include "config.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;
+
+static void configfile(void);
+static void get_srcdir(void);
+static void usage(void);
+static void cleanheaders(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];
+
+ while ((ch = getopt(argc, argv, "d:gp")) != -1)
+ switch (ch) {
+ 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 '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (freopen(PREFIX = *argv, "r", stdin) == NULL)
+ err(2, "%s", 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 ((buf.st_mode & S_IFMT) != S_IFDIR)
+ errx(2, "%s isn't a directory", p);
+
+ STAILQ_INIT(&dtab);
+ STAILQ_INIT(&fntab);
+ SLIST_INIT(&cputype);
+ STAILQ_INIT(&ftab);
+ yyfile = *argv;
+ if (yyparse())
+ exit(3);
+ if (machinename == NULL) {
+ printf("Specify machine type, e.g. ``machine i386''\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"));
+ options(); /* make options .h files */
+ makefile(); /* build Makefile */
+ headers(); /* make a lot of .h files */
+ configfile(); /* put config file into kernel*/
+ cleanheaders(p);
+ printf("Kernel build directory is %s\n", p);
+ printf("Don't forget to do a ``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)
+{
+
+ if (realpath("../..", srcdir) == NULL)
+ errx(2, "Unable to find root of source tree");
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: config [-gp] [-d destdir] sysname\n");
+ exit(1);
+}
+
+/*
+ * 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);
+}
+
+static void
+configfile(void)
+{
+ FILE *fi, *fo;
+ char *p;
+ int i;
+
+ fi = fopen(PREFIX, "r");
+ if (!fi)
+ err(2, "%s", PREFIX);
+ fo = fopen(p=path("config.c.new"), "w");
+ if (!fo)
+ err(2, "%s", p);
+ fprintf(fo, "#include \"opt_config.h\"\n");
+ fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
+ fprintf(fo, "static const char config[] = \"\\\n");
+ fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
+ while (EOF != (i=getc(fi))) {
+ if (i == '\n') {
+ fprintf(fo, "\\n\\\n___");
+ } else if (i == '\"') {
+ fprintf(fo, "\\\"");
+ } else if (i == '\\') {
+ fprintf(fo, "\\\\");
+ } else {
+ putc(i, fo);
+ }
+ }
+ fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
+ fprintf(fo, "\";\n");
+ fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
+ fclose(fi);
+ fclose(fo);
+ moveifchanged(path("config.c.new"), path("config.c"));
+}
+
+/*
+ * 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);
+#ifndef MAP_FAILED
+#define MAP_FAILED ((caddr_t) -1)
+#endif
+ 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 (strcmp(dp->d_name, hl->h_name) == 0) {
+ 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 (strcmp(s, hl->h_name) == 0) {
+ free(s);
+ return;
+ }
+ }
+ hl = malloc(sizeof(*hl));
+ bzero(hl, sizeof(*hl));
+ hl->h_name = s;
+ hl->h_next = htab;
+ htab = hl;
+}
diff --git a/usr.sbin/config/mkheaders.c b/usr.sbin/config/mkheaders.c
new file mode 100644
index 0000000..bdc8599
--- /dev/null
+++ b/usr.sbin/config/mkheaders.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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[] = "@(#)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 int do_header(char *, int);
+static char *toheader(char *);
+static char *tomacro(char *);
+
+void
+headers(void)
+{
+ struct file_list *fl;
+ struct device *dp;
+ int match;
+ int errors;
+
+ errors = 0;
+ STAILQ_FOREACH(fl, &ftab, f_next) {
+ if (fl->f_needs != 0) {
+ match = 0;
+ STAILQ_FOREACH(dp, &dtab, d_next) {
+ if (eq(dp->d_name, fl->f_needs)) {
+ match++;
+ dp->d_done |= DEVDONE;
+ }
+ }
+ if (fl->f_flags & NEED_COUNT)
+ errors += do_header(fl->f_needs, match);
+ }
+ }
+ STAILQ_FOREACH(dp, &dtab, d_next) {
+ if (!(dp->d_done & DEVDONE)) {
+ warnx("Error: device \"%s\" is unknown",
+ dp->d_name);
+ errors++;
+ }
+ if (dp->d_count == UNKNOWN)
+ continue;
+ match = 0;
+ STAILQ_FOREACH(fl, &ftab, f_next) {
+ if (fl->f_needs == 0)
+ continue;
+ if ((fl->f_flags & NEED_COUNT) == 0)
+ continue;
+ if (eq(dp->d_name, fl->f_needs)) {
+ match++;
+ break;
+ }
+ }
+ if (match == 0) {
+ warnx("Error: device \"%s\" does not take a count",
+ dp->d_name);
+ errors++;
+ }
+ }
+ if (errors)
+ errx(1, "%d errors", errors);
+}
+
+static int
+do_header(char *dev, int match)
+{
+ char *file, *name, *inw;
+ struct file_list *fl, *tflp;
+ struct file_list_head fl_head;
+ struct device *dp;
+ FILE *inf, *outf;
+ int inc, oldcount;
+ int count, hicount;
+ int errors;
+
+ /*
+ * After this loop, "count" will be the actual number of units,
+ * and "hicount" will be the highest unit declared. do_header()
+ * must use this higher of these values.
+ */
+ errors = 0;
+ hicount = count = 0;
+ STAILQ_FOREACH(dp, &dtab, d_next) {
+ if (eq(dp->d_name, dev)) {
+ if (dp->d_count == UNKNOWN) {
+ warnx("Device \"%s\" requires a count", dev);
+ return 1;
+ }
+ count = dp->d_count;
+ break;
+ }
+ }
+ file = toheader(dev);
+ name = tomacro(dev);
+ if (match)
+ fprintf(stderr,
+ "FYI: static unit limits for %s are set: %s=%d\n",
+ dev, name, count);
+ remember(file);
+ inf = fopen(file, "r");
+ oldcount = -1;
+ if (inf == 0) {
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ fprintf(outf, "#define %s %d\n", name, count);
+ (void) fclose(outf);
+ return 0;
+ }
+ STAILQ_INIT(&fl_head);
+ for (;;) {
+ char *cp;
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ inw = ns(inw);
+ cp = get_word(inf);
+ if (cp == 0 || cp == (char *)EOF)
+ break;
+ inc = atoi(cp);
+ if (eq(inw, name)) {
+ oldcount = inc;
+ inc = count;
+ }
+ cp = get_word(inf);
+ if (cp == (char *)EOF)
+ break;
+ fl = (struct file_list *) malloc(sizeof *fl);
+ bzero(fl, sizeof(*fl));
+ fl->f_fn = inw; /* malloced */
+ fl->f_type = inc;
+ STAILQ_INSERT_HEAD(&fl_head, fl, f_next);
+ }
+ (void) fclose(inf);
+ if (count == oldcount) {
+ for (fl = STAILQ_FIRST(&fl_head); fl != NULL; fl = tflp) {
+ tflp = STAILQ_NEXT(fl, f_next);
+ free(fl->f_fn);
+ free(fl);
+ }
+ return 0;
+ }
+ if (oldcount == -1) {
+ fl = (struct file_list *) malloc(sizeof *fl);
+ bzero(fl, sizeof(*fl));
+ fl->f_fn = ns(name);
+ fl->f_type = count;
+ STAILQ_INSERT_HEAD(&fl_head, fl, f_next);
+ }
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ for (fl = STAILQ_FIRST(&fl_head); fl != NULL; fl = tflp) {
+ fprintf(outf,
+ "#define %s %u\n", fl->f_fn, count ? fl->f_type : 0);
+ tflp = STAILQ_NEXT(fl, f_next);
+ free(fl->f_fn);
+ free(fl);
+ }
+ (void) fclose(outf);
+ return 0;
+}
+
+/*
+ * convert a dev name to a .h file name
+ */
+static char *
+toheader(char *dev)
+{
+ static char hbuf[MAXPATHLEN];
+
+ snprintf(hbuf, sizeof(hbuf), "%s.h", path(dev));
+ return (hbuf);
+}
+
+/*
+ * convert a dev name to a macro name
+ */
+static char *
+tomacro(char *dev)
+{
+ static char mbuf[20];
+ char *cp;
+
+ cp = mbuf;
+ *cp++ = 'N';
+ while (*dev)
+ *cp++ = islower(*dev) ? toupper(*dev++) : *dev++;
+ *cp++ = 0;
+ return (mbuf);
+}
diff --git a/usr.sbin/config/mkmakefile.c b/usr.sbin/config/mkmakefile.c
new file mode 100644
index 0000000..63bd348
--- /dev/null
+++ b/usr.sbin/config/mkmakefile.c
@@ -0,0 +1,794 @@
+/*
+ * 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.
+ * 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[] = "@(#)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 *) malloc(sizeof *fp);
+ bzero(fp, 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;
+ int versreq;
+ char *s;
+
+ 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);
+
+ /* XXX this check seems to be misplaced. */
+ if (SLIST_EMPTY(&cputype)) {
+ printf("cpu type must be specified\n");
+ exit(1);
+ }
+
+ ofp = fopen(path("Makefile.new"), "w");
+ if (ofp == 0)
+ err(1, "%s", path("Makefile.new"));
+ fprintf(ofp, "KERN_IDENT=%s\n", ident);
+ SLIST_FOREACH(op, &mkopt, op_next)
+ fprintf(ofp, "%s=%s\n", op->op_name, op->op_value);
+ 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 (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"));
+
+ /* XXX makefile() should make the Makefile, not hints.c. */
+ if (hints) {
+ ifp = fopen(hints, "r");
+ if (ifp == NULL)
+ err(1, "%s", hints);
+ } else {
+ ifp = NULL;
+ }
+ 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");
+ 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("hints.c.new"), path("hints.c"));
+
+ /* XXX makefile() should make the Makefile, not env.c. */
+ 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)
+{
+ FILE *fp;
+ struct file_list *tp, *pf;
+ struct device *dp;
+ struct opt *op;
+ char *wd, *this, *needs, *compilewith, *depends, *clean, *warning;
+ int nreqs, isdup, std, filetype,
+ imp_rule, no_obj, needcount, before_depend, mandatory, nowerror;
+
+ fp = fopen(fname, "r");
+ if (fp == 0)
+ err(1, "%s", fname);
+next:
+ /*
+ * filename [ standard | mandatory | optional | count ]
+ * [ 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;
+ }
+ this = ns(wd);
+ next_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: No type for %s.\n",
+ fname, this);
+ exit(1);
+ }
+ if ((pf = fl_lookup(this)) && (pf->f_type != INVISIBLE || pf->f_flags))
+ isdup = ISDUP;
+ else
+ isdup = 0;
+ tp = 0;
+ nreqs = 0;
+ compilewith = 0;
+ depends = 0;
+ clean = 0;
+ warning = 0;
+ needs = 0;
+ std = mandatory = 0;
+ imp_rule = 0;
+ no_obj = 0;
+ needcount = 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, "count")) {
+ needcount = 1;
+ } else if (!eq(wd, "optional")) {
+ printf("%s: %s must be count, optional, mandatory or standard\n",
+ fname, this);
+ exit(1);
+ }
+nextparam:
+ next_word(fp, wd);
+ if (wd == 0) {
+ if (isdup)
+ goto next;
+ goto doneparam;
+ }
+ 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;
+ }
+ if (needs == 0 && nreqs == 1)
+ needs = ns(wd);
+ if (isdup)
+ goto invis;
+ STAILQ_FOREACH(dp, &dtab, d_next)
+ if (eq(dp->d_name, wd)) {
+ if (std && dp->d_count <= 0)
+ dp->d_count = 1;
+ 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)) {
+ if (nreqs == 1) {
+ free(needs);
+ needs = 0;
+ }
+ goto nextparam;
+ }
+invis:
+ while ((wd = get_word(fp)) != 0)
+ ;
+ if (tp == 0)
+ tp = new_fent();
+ tp->f_fn = this;
+ tp->f_type = INVISIBLE;
+ tp->f_needs = needs;
+ tp->f_flags |= isdup;
+ if (needcount)
+ tp->f_flags |= NEED_COUNT;
+ tp->f_compilewith = compilewith;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ tp->f_warn = warning;
+ goto next;
+
+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;
+ if (tp == 0)
+ tp = new_fent();
+ tp->f_fn = this;
+ tp->f_type = filetype;
+ tp->f_flags &= ~ISDUP;
+ 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 (needcount)
+ tp->f_flags |= NEED_COUNT;
+ if (nowerror)
+ tp->f_flags |= NOWERROR;
+ tp->f_needs = needs;
+ tp->f_compilewith = compilewith;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ tp->f_warn = warning;
+ if (pf && pf->f_type == INVISIBLE)
+ pf->f_flags |= ISDUP; /* mark as duplicate */
+ 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;
+
+ if (ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+ (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_type == INVISIBLE || 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 != INVISIBLE && 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, *tp;
+ struct file_list *ftp;
+ char *compilewith;
+
+ STAILQ_FOREACH(ftp, &ftab, f_next) {
+ if (ftp->f_type == INVISIBLE)
+ continue;
+ 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);
+ }
+ }
+ tp = tail(np);
+ 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}", 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..f5b317a
--- /dev/null
+++ b/usr.sbin/config/mkoptions.c
@@ -0,0 +1,370 @@
+/*
+ * 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.
+ * 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[] = "@(#)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 *)malloc(sizeof(*op));
+ memset(op, 0, 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 *)malloc(sizeof(*op));
+ memset(op, 0, 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 *) malloc(sizeof *op);
+ bzero(op, 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 *) malloc(sizeof *op);
+ bzero(op, 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);
+ if (ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+ (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 *) malloc(sizeof *po);
+ bzero(po, 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/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..d7844f5
--- /dev/null
+++ b/usr.sbin/cron/Makefile.inc
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.if exists(${.OBJDIR}/../lib)
+LIBCRON= ${.OBJDIR}/../lib/libcron.a
+.else
+LIBCRON= ${.CURDIR}/../lib/libcron.a
+.endif
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/cron/cron/Makefile b/usr.sbin/cron/cron/Makefile
new file mode 100644
index 0000000..d578da7
--- /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
+
+DPADD= ${LIBCRON} ${LIBUTIL}
+LDADD= ${LIBCRON} -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..8ad0622
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.8
@@ -0,0 +1,177 @@
+.\"/* 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 20, 1993
+.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 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 don't 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 ) .
+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. 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 won't 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 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's 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 SEE ALSO
+.Xr crontab 1 ,
+.Xr crontab 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..17c6894
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.c
@@ -0,0 +1,449 @@
+/* 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 __P((void)),
+ run_reboot_jobs __P((cron_db *)),
+ cron_tick __P((cron_db *)),
+ cron_sync __P((void)),
+ cron_sleep __P((cron_db *)),
+ cron_clean __P((cron_db *)),
+#ifdef USE_SIGCHLD
+ sigchld_handler __P((int)),
+#endif
+ sighup_handler __P((int)),
+ parse_args __P((int c, char *v[]));
+
+static time_t last_time = 0;
+static int dst_enabled = 0;
+
+static void
+usage() {
+ char **dflags;
+
+ fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] "
+ "[-s] [-o] [-x debugflag[,...]]\n");
+ fprintf(stderr, "\ndebugflags: ");
+
+ for(dflags = DebugFlagNames; *dflags; dflags++) {
+ fprintf(stderr, "%s ", *dflags);
+ }
+ fprintf(stderr, "\n");
+
+ exit(ERROR_EXIT);
+}
+
+
+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);
+
+ acquire_daemonlock(0);
+ 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) {
+ log_it("CRON",getpid(),"DEATH","can't become daemon");
+ exit(0);
+ }
+ }
+
+ acquire_daemonlock(0);
+ 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(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(x) {
+ log_close();
+}
+
+
+static void
+parse_args(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int argch;
+ char *endp;
+
+ while ((argch = getopt(argc, argv, "j:J: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 '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..f7b2b7a
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.h
@@ -0,0 +1,296 @@
+/* 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 <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 */
+
+ /* 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 __P((void)),
+ set_cron_cwd __P((void)),
+ load_database __P((cron_db *)),
+ open_logfile __P((void)),
+ sigpipe_func __P((void)),
+ job_add __P((entry *, user *)),
+ do_command __P((entry *, user *)),
+ link_user __P((cron_db *, user *)),
+ unlink_user __P((cron_db *, user *)),
+ free_user __P((user *)),
+ env_free __P((char **)),
+ unget_char __P((int, FILE *)),
+ free_entry __P((entry *)),
+ acquire_daemonlock __P((int)),
+ skip_comments __P((FILE *)),
+ log_it __P((char *, int, char *, char *)),
+ log_close __P((void));
+
+int job_runqueue __P((void)),
+ set_debug_flags __P((char *)),
+ get_char __P((FILE *)),
+ get_string __P((char *, int, FILE *, char *)),
+ swap_uids __P((void)),
+ load_env __P((char *, FILE *)),
+ cron_pclose __P((FILE *)),
+ strcmp_until __P((char *, char *, int)),
+ allowed __P((char *)),
+ strdtb __P((char *));
+
+char *env_get __P((char *, char **)),
+ *arpadate __P((time_t *)),
+ *mkprints __P((unsigned char *, unsigned int)),
+ *first_word __P((char *, char *)),
+ **env_init __P((void)),
+ **env_copy __P((char **)),
+ **env_set __P((char **, char *));
+
+user *load_user __P((int, struct passwd *, char *)),
+ *find_user __P((cron_db *, char *));
+
+entry *load_entry __P((FILE *, void (*)(),
+ struct passwd *, char **));
+
+FILE *cron_popen __P((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;
+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;
+extern int LineNumber;
+extern unsigned Jitter,
+ RootJitter;
+extern time_t TargetTime;
+# 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..9ebf75d
--- /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 __P((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", "*system*",
+ 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, "*system*") && !(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..592ca50
--- /dev/null
+++ b/usr.sbin/cron/cron/do_command.c
@@ -0,0 +1,556 @@
+/* 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
+
+
+static void child_process __P((entry *, user *)),
+ do_univ __P((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 */
+ acquire_daemonlock(1);
+ 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 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 privledges.
+ */
+ setgid(e->gid);
+# if defined(BSD)
+ initgroups(usernm, e->gid);
+# endif
+ setlogin(usernm);
+ setuid(e->uid); /* 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) {
+ /* MAILTO was present in the environment
+ */
+ if (!*mailto) {
+ /* ... but it's empty. set to NULL
+ */
+ mailto = NULL;
+ }
+ } else {
+ /* MAILTO not present, set to USER.
+ */
+ mailto = usernm;
+ }
+
+ /* 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..3efe605
--- /dev/null
+++ b/usr.sbin/cron/cron/externs.h
@@ -0,0 +1,145 @@
+/* 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 __P((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 __P((char *, char *));
+#endif
+
+#ifdef NEED_STRDUP
+extern char *strdup __P((char *));
+#endif
+
+#ifdef NEED_STRERROR
+extern char *strerror __P((int));
+#endif
+
+#ifdef NEED_FLOCK
+extern int flock __P((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 __P((void));
+#endif
+
+#ifdef NEED_GETDTABLESIZE
+extern int getdtablesize __P((void));
+#endif
+
+#ifdef NEED_SETENV
+extern int setenv __P((char *, char *, int));
+#endif
+
+#ifdef NEED_VFORK
+extern PID_T vfork __P((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..806676d
--- /dev/null
+++ b/usr.sbin/cron/cron/popen.c
@@ -0,0 +1,240 @@
+/*
+ * 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 privledges.
+ */
+ setgid(e->gid);
+# if defined(BSD)
+ initgroups(usernm, e->gid);
+# endif
+ setlogin(usernm);
+ setuid(e->uid); /* 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..96a4bd6
--- /dev/null
+++ b/usr.sbin/cron/crontab/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+BINDIR= /usr/bin
+
+PROG= crontab
+MAN= crontab.1 crontab.5
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+CFLAGS+= -I${.CURDIR}/../cron
+
+DPADD= ${LIBCRON} ${LIBUTIL}
+LDADD= ${LIBCRON} -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..afc50ea
--- /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 SEE ALSO
+.Xr crontab 5 ,
+.Xr cron 8
+.Sh FILES
+.Bl -tag -width /var/cron/allow -compact
+.It Pa /var/cron/allow
+.It Pa /var/cron/deny
+.El
+.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 DIAGNOSTICS
+A fairly informative usage message appears if you run it with a bad command
+line.
+.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..d1eaa4a
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.5
@@ -0,0 +1,280 @@
+.\"/* 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 January 24, 1994
+.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 ,
+and
+.Ev LOGNAME
+and
+.Ev HOME
+are set from the
+.Pa /etc/passwd
+line of the crontab's owner.
+.Ev HOME
+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 ,
+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 seperating 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
+doesn't do aliasing, and UUCP
+usually doesn't 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 doesn't 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, aren't *), 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
+can't do this), can be
+mailed to a person other than the crontab owner (SysV can't do this), or the
+feature can be turned off and no mail will be sent at all (SysV can't 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're 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's 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..a347a9f
--- /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 <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 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 __P((void)),
+ delete_cmd __P((void)),
+ edit_cmd __P((void)),
+ poke_daemon __P((void)),
+ check_error __P((char *)),
+ parse_args __P((int c, char *v[]));
+static int replace_cmd __P((void));
+
+
+static void
+usage(msg)
+ 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(argc, argv)
+ 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;
+
+ if (!(pw = getpwuid(getuid())))
+ errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out");
+ (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);
+ (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 {
+ /* 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() < OK)
+ err(ERROR_EXIT, "swapping uids back");
+ }
+ }
+
+ Debug(DMISC, ("user=%s, file=%s, option=%s\n",
+ User, Filename, Options[(int)Option]))
+}
+
+
+static void
+list_cmd() {
+ char n[MAX_FNAME];
+ FILE *f;
+ int ch, x;
+
+ log_it(RealUser, Pid, "LIST", User);
+ (void) sprintf(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.
+ */
+ Set_LineNum(1)
+
+ /* ignore the top few comments since we probably put them there.
+ */
+ for (x = 0; x < NHEADER_LINES; x++) {
+ ch = get_char(f);
+ if (EOF == ch)
+ break;
+ if ('#' != ch) {
+ putchar(ch);
+ break;
+ }
+ while (EOF != (ch = get_char(f)))
+ if (ch == '\n')
+ break;
+ if (EOF == ch)
+ break;
+ }
+
+ while (EOF != (ch = get_char(f)))
+ putchar(ch);
+ 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) sprintf(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 ch, t, x;
+ struct stat statbuf, fsbuf;
+ time_t mtime;
+ WAIT_T waiter;
+ PID_T pid, xpid;
+ mode_t um;
+
+ log_it(RealUser, Pid, "BEGIN EDIT", User);
+ (void) sprintf(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) sprintf(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;
+ }
+
+ Set_LineNum(1)
+
+ /* ignore the top few comments since we probably put them there.
+ */
+ for (x = 0; x < NHEADER_LINES; x++) {
+ ch = get_char(f);
+ if (EOF == ch)
+ break;
+ if ('#' != ch) {
+ putc(ch, NewCrontab);
+ break;
+ }
+ while (EOF != (ch = get_char(f)))
+ if (ch == '\n')
+ break;
+ if (EOF == ch)
+ break;
+ }
+
+ /* copy the rest of the crontab (if any) to the temp file.
+ */
+ if (EOF != ch)
+ while (EOF != (ch = get_char(f)))
+ putc(ch, 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");
+ mtime = statbuf.st_mtime;
+
+ 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 (*f[4])();
+ f[0] = signal(SIGHUP, SIG_IGN);
+ f[1] = signal(SIGINT, SIG_IGN);
+ f[2] = signal(SIGTERM, SIG_IGN);
+ xpid = wait(&waiter);
+ signal(SIGHUP, f[0]);
+ signal(SIGINT, f[1]);
+ signal(SIGTERM, f[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 (mtime == statbuf.st_mtime) {
+ warnx("no changes made to crontab");
+ goto remove;
+ }
+ warnx("installing new crontab");
+ switch (replace_cmd()) {
+ case 0:
+ break;
+ case -1:
+ 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':
+ goto again;
+ case 'n':
+ goto abandon;
+ default:
+ fprintf(stderr, "Enter Y or N\n");
+ }
+ }
+ /*NOTREACHED*/
+ case -2:
+ 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) sprintf(n, "tmp.%d", Pid);
+ (void) sprintf(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) sprintf(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..ed14dfc
--- /dev/null
+++ b/usr.sbin/cron/lib/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+LIB= cron
+INTERNALLIB= YES
+SRCS= entry.c env.c misc.c
+
+CFLAGS+= -I${.CURDIR}/../cron
+CFLAGS+= -DLOGIN_CAP
+
+.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..2b886ee
--- /dev/null
+++ b/usr.sbin/cron/lib/entry.c
@@ -0,0 +1,636 @@
+/* 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 __P((bitstr_t *, int, int, char *[], int, FILE *)),
+ get_range __P((bitstr_t *, int, int, char *[], int, FILE *)),
+ get_number __P((int *, int, char *[], int, FILE *));
+static int set_element __P((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)();
+ 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
+ }
+
+ if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
+ ecode = e_username;
+ goto eof;
+ }
+
+ 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;
+ }
+ }
+ 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 != '-') {
+ /* 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)
+ 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';
+
+ /* 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..89f15e4
--- /dev/null
+++ b/usr.sbin/cron/lib/misc.c
@@ -0,0 +1,662 @@
+/* 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);
+}
+
+
+/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
+ * another daemon is already running, which we detect here.
+ *
+ * note: main() calls us twice; once before forking, once after.
+ * we maintain static storage of the file pointer so that we
+ * can rewrite our PID into the PIDFILE after the fork.
+ *
+ * it would be great if fflush() disassociated the file buffer.
+ */
+void
+acquire_daemonlock(closeflag)
+ int closeflag;
+{
+ static FILE *fp = NULL;
+
+ if (closeflag && fp) {
+ fclose(fp);
+ fp = NULL;
+ return;
+ }
+
+ if (!fp) {
+ char pidfile[MAX_FNAME];
+ char buf[MAX_TEMPSTR];
+ int fd, otherpid;
+
+ (void) sprintf(pidfile, PIDFILE, PIDDIR);
+ if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644)))
+ || (NULL == (fp = fdopen(fd, "r+")))
+ ) {
+ sprintf(buf, "can't open or create %s: %s",
+ pidfile, strerror(errno));
+ log_it("CRON", getpid(), "DEATH", buf);
+ errx(ERROR_EXIT, "%s", buf);
+ }
+
+ if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
+ int save_errno = errno;
+
+ fscanf(fp, "%d", &otherpid);
+ sprintf(buf, "can't lock %s, otherpid may be %d: %s",
+ pidfile, otherpid, strerror(save_errno));
+ log_it("CRON", getpid(), "DEATH", buf);
+ errx(ERROR_EXIT, "%s", buf);
+ }
+
+ (void) fcntl(fd, F_SETFD, 1);
+ }
+
+ rewind(fp);
+ fprintf(fp, "%d\n", getpid());
+ fflush(fp);
+ (void) ftruncate(fileno(fp), ftell(fp));
+
+ /* abandon fd and fp even though the file is open. we need to
+ * keep it open and locked, but we don't need the handles elsewhere.
+ */
+}
+
+/* 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(string, 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;
+
+#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;
+ deny = 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.
+ */
+void
+mkprint(dst, src, len)
+ 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..2cf9576
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunched_main.c
@@ -0,0 +1,119 @@
+/*
+ * 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 <string.h>
+
+struct stub {
+ char *name;
+ int (*f)();
+};
+
+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)
+{
+ struct stub *ep;
+ int columns, len;
+
+ if(argc <= 1)
+ crunched_usage();
+
+ 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..4333866
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.1
@@ -0,0 +1,448 @@
+.\"
+.\" 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 November 16, 2000
+.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 behaviour 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 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.
+.Sh SEE ALSO
+.Xr crunchide 1 ,
+.Xr make 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
+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.
diff --git a/usr.sbin/crunch/crunchgen/crunchgen.c b/usr.sbin/crunch/crunchgen/crunchgen.c
new file mode 100644
index 0000000..078b14f
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.c
@@ -0,0 +1,1128 @@
+/*
+ * 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;
+ int goterror;
+} prog_t;
+
+
+/* global state */
+
+strlst_t *buildopts = NULL;
+strlst_t *srcdirs = NULL;
+strlst_t *libs = 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);
+
+/* 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_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], "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->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]);
+}
+
+
+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);
+ if (buildopts) {
+ fprintf(f, "BUILDOPTS+=");
+ output_strlst(f, buildopts);
+ }
+ fprintf(f, ".if defined(PROG) && !defined(%s)\n", objvar);
+ 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 crunchgen_objs 2>&1",
+ 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;
+
+ fprintf(outmk, "LIBS+=");
+ output_strlst(outmk, libs);
+
+ if (makeobj) {
+ fprintf(outmk, "MAKEOBJDIRPREFIX?=%s\n", objprefix);
+ fprintf(outmk, "MAKE=env MAKEOBJDIRPREFIX=$(MAKEOBJDIRPREFIX) "
+ "make\n");
+ } else {
+ fprintf(outmk, "MAKE=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)\n", execfname, execfname);
+ fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
+ execfname, execfname);
+ 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");
+
+ 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);
+ }
+ fprintf(outmk, "%s_make:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident);
+ if (makeobj)
+ fprintf(outmk, "$(MAKE) obj && ");
+ fprintf(outmk, "\\\n");
+ fprintf(outmk, "\t\t$(MAKE) $(BUILDOPTS) $(%s_OPTS) depend &&",
+ p->ident);
+ fprintf(outmk, "\\\n");
+ fprintf(outmk, "\t\t$(MAKE) $(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) && $(MAKE) $(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);
+ }
+
+ 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->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)
+ 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 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..945ed5b
--- /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} == alpha || ${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..24bd60a
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.1
@@ -0,0 +1,86 @@
+.\"
+.\" 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
+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 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..f50d3e5
--- /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 __P((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, 1024, 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..6aa1b8b
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_aout.c
@@ -0,0 +1,197 @@
+/* $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/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..5558b1c
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_elf32.c
@@ -0,0 +1,425 @@
+/*
+ * 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_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_Word *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_Word), fn,
+ "symbol forward mapping table")) == NULL)
+ goto bad;
+ if ((symrvmap = xmalloc(nsyms * sizeof (Elf_Word), 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_Word 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..5ff5263
--- /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
+
+NOMAN= #true
+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..f8590ae
--- /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_fdescfs mount_isofs
+progs mount_lofs mount_msdosfs mount_portalfs mount_procfs 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 mrouted 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..cdc75ab
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.1
@@ -0,0 +1,301 @@
+.\"----------------------------------------------------------------------------
+.\""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, don't 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 don't 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..6165c2e
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.5
@@ -0,0 +1,181 @@
+.\"----------------------------------------------------------------------------
+.\""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 isn't 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..f12ecea
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ctm_rmail
+
+PROG= ctm_dequeue
+NOMAN= #true
+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..3943fd2
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.1
@@ -0,0 +1,487 @@
+.\" 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 .
+Don't 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 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.
+.\" This next request is for sections 1, 6, 7 & 8 only
+.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 DIAGNOSTICS
+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.
+.Pp
+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..c2d0789
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ctm_rmail
+
+PROG= ctm_smail
+NOMAN= #true
+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..6bf5aeb
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/Makefile
@@ -0,0 +1,27 @@
+# $FreeBSD$
+
+PROG= mkctm
+NOMAN= 1
+SRCS= mkctm.c
+
+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/daemon/Makefile b/usr.sbin/daemon/Makefile
new file mode 100644
index 0000000..3480393
--- /dev/null
+++ b/usr.sbin/daemon/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= daemon
+MAN= daemon.8
+
+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..23702db
--- /dev/null
+++ b/usr.sbin/daemon/daemon.8
@@ -0,0 +1,81 @@
+.\" 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 August 30, 2001
+.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
+.Ar command arguments ...
+.Sh DESCRIPTION
+The
+.Nm
+utility detaches itself from the controlling terminal and
+executes the program specified by its arguments.
+.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 .
+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).
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits 1 if an error is returned by the
+.Xr daemon 3
+library routine, 2 if the pid-file is requested, but can not be opened,
+otherwise 0.
+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 daemon 3 ,
+.Xr exec 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..b30a7f4
--- /dev/null
+++ b/usr.sbin/daemon/daemon.c
@@ -0,0 +1,112 @@
+/*-
+ * 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/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, nochdir, noclose, errcode;
+ FILE *pidf;
+ const char *pidfile;
+
+ nochdir = noclose = 1;
+ pidfile = NULL;
+ while ((ch = getopt(argc, argv, "-cfp:")) != -1) {
+ switch (ch) {
+ case 'c':
+ nochdir = 0;
+ break;
+ case 'f':
+ noclose = 0;
+ break;
+ case 'p':
+ pidfile = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+ /*
+ * Try to open the pidfile before calling daemon(3),
+ * to be able to report the error intelligently
+ */
+ if (pidfile) {
+ pidf = fopen(pidfile, "w");
+ if (pidf == NULL)
+ 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) {
+ fprintf(pidf, "%lu\n", (unsigned long)getpid());
+ fclose(pidf);
+ }
+
+ execvp(argv[0], argv);
+
+ /*
+ * execvp() failed -- unlink pidfile if any, and
+ * report the error
+ */
+ errcode = errno; /* Preserve errcode -- unlink may reset it */
+ if (pidfile)
+ unlink(pidfile);
+
+ /* The child is now running, so the exit status doesn't matter. */
+ errc(1, errcode, "%s", argv[0]);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: daemon [-cf] [-p pidfile] command arguments ...\n");
+ exit(1);
+}
diff --git a/usr.sbin/dconschat/Makefile b/usr.sbin/dconschat/Makefile
new file mode 100644
index 0000000..9993ece
--- /dev/null
+++ b/usr.sbin/dconschat/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= dconschat
+SRCS= dconschat.c
+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..f560740
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.8
@@ -0,0 +1,314 @@
+.\" 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 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.
+.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.
+To quit
+.Nm ,
+send a CR +
+.Ql ~
++
+.Ql \&.
+sequence to the console port,
+or send signal to the process.
+.Pp
+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 .
+.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 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 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 FILES
+.Bl -tag -width indent -compact
+.It Pa /dev/fwmem0.0
+.It Pa /dev/mem
+.It Pa /dev/kmem
+.El
+.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..d343811
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.c
@@ -0,0 +1,964 @@
+/*
+ * 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;
+
+#define IS_CONSOLE(p) ((p)->port == 0)
+#define IS_GDB(p) ((p)->port == 1)
+
+static struct dcons_state {
+ int fd;
+ kvm_t *kd;
+ int kq;
+ off_t paddr;
+#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;
+ 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;
+} sc;
+
+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_cleanup(int sig)
+{
+ struct dcons_state *dc;
+
+ 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");
+ 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;
+ 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");
+ return (-1);
+ }
+ 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:
+ if (reg->key == DCONS_CSR_KEY_HI)
+ hi = reg->val;
+ else if (reg->key == DCONS_CSR_KEY_LO) {
+ lo = reg->val;
+ goto out;
+ }
+ break;
+ }
+ }
+ /* not found */
+ return (-1);
+out:
+ if (verbose)
+ printf("addr: %06x %06x\n", hi, lo);
+ dc->paddr = ((off_t)hi << 24) | lo;
+ 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;
+
+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)) {
+ dconschat_ready(dc, 0, "wrong magic");
+ 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)
+ 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]", 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->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) {
+ struct termios traw;
+
+ traw = dc->tsave;
+ cfmakeraw(&traw);
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &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)
+{
+ int foo, ns, flags;
+ struct kevent kev;
+
+ /* accept connection */
+ foo = p->res->ai_addrlen;
+ ns = accept(p->s, p->res->ai_addr, &foo);
+ 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
+
+ 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)
+{
+ static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
+
+ while (slen > 0) {
+ if (IS_CONSOLE(p)) {
+ if ((dc->flags & F_TELNET) != 0) {
+ /* XXX Telent 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 == KEY_TILDE)
+ dc->escape_state = STATE2;
+ else
+ dc->escape_state = STATE0;
+ break;
+ case STATE2:
+ dc->escape_state = STATE0;
+ if (*sp == '.')
+ dconschat_cleanup(0);
+ }
+ if (*sp == KEY_CR)
+ dc->escape_state = STATE1;
+ } else if (IS_GDB(p)) {
+ /* GDB: ^C -> CR+~+^B */
+ if (*sp == 0x3 && (dc->flags & F_ALT_BREAK) != 0) {
+ bcopy(abreak, dp, 3);
+ dp += 3;
+ sp ++;
+ *dlen += 3;
+ /* discard rest of the packet */
+ slen = 0;
+ break;
+ }
+ }
+ *dp++ = *sp++;
+ (*dlen) ++;
+ 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);
+ }
+ if (verbose) {
+ 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);
+ dconschat_get_ptr(dc);
+ }
+ 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;
+
+ while (1) {
+ if ((dc->flags & F_READY) == 0 &&
+ (++counter % (poll_hz * DCONS_POLL_OFFLINE)) == 0)
+ dconschat_fetch_header(dc);
+ if ((dc->flags & F_READY) != 0)
+ 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;
+
+ /* defualt ports */
+ port[0] = 0; /* stdin/out for console */
+ port[1] = -1; /* disable gdb port */
+
+ while ((ch = getopt(argc, argv, "a:bh: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 '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);
+
+ /* iniit 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..70976db
--- /dev/null
+++ b/usr.sbin/devinfo/devinfo.8
@@ -0,0 +1,72 @@
+.\" -*- 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 May 10, 2002
+.Os
+.Dt DEVINFO 8
+.Sh NAME
+.Nm devinfo
+.Nd print information about system device configuration
+.Sh SYNOPSIS
+.Nm
+.Op Fl ruv
+.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
+.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..e1cb4bf
--- /dev/null
+++ b/usr.sbin/devinfo/devinfo.c
@@ -0,0 +1,229 @@
+/*-
+ * 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 <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 > 100) || (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:
+ errx(1, "usage: %s [-ruv]", argv[0]);
+ }
+ }
+
+ 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..cc99fd2
--- /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 BUGS
+It should be possible to reinitialize a board without closing all of the
+existing ports.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
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..a89c25d
--- /dev/null
+++ b/usr.sbin/diskinfo/diskinfo.8
@@ -0,0 +1,69 @@
+.\"
+.\" 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 April 9, 2003
+.Dt DISKINFO 8
+.Os
+.Sh NAME
+.Nm diskinfo
+.Nd get information about disk device
+.Sh SYNOPSIS
+.Nm
+.Op Fl tv
+.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 t
+option triggers a simple and rather naive benchmark of the disks seek
+and transfer performance.
+.Sh AUTHORS
+.An Poul-Henning Kamp
+.Sh BUGS
+There are in order of increasing severity: lies,
+damn lies, statistics, and computer benchmarks.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 5.1 .
diff --git a/usr.sbin/diskinfo/diskinfo.c b/usr.sbin/diskinfo/diskinfo.c
new file mode 100644
index 0000000..5203a7b
--- /dev/null
+++ b/usr.sbin/diskinfo/diskinfo.c
@@ -0,0 +1,310 @@
+/*-
+ * 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 [-tv] disk ...\n");
+ exit (1);
+}
+
+static int opt_t, opt_v;
+
+static void speeddisk(int fd, off_t mediasize, u_int sectorsize);
+
+int
+main(int argc, char **argv)
+{
+ int i, ch, fd, error;
+ char buf[BUFSIZ];
+ off_t mediasize;
+ u_int sectorsize, fwsectors, fwheads;
+
+ while ((ch = getopt(argc, argv, "tv")) != -1) {
+ switch (ch) {
+ 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;
+ 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);
+ }
+ }
+ printf("\n");
+ 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;
+}
diff --git a/usr.sbin/editmap/Makefile b/usr.sbin/editmap/Makefile
new file mode 100644
index 0000000..b141bfd
--- /dev/null
+++ b/usr.sbin/editmap/Makefile
@@ -0,0 +1,49 @@
+# $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
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmdb)
+LIBSMDBDIR:= ${.OBJDIR}/../../lib/libsmdb
+.else
+LIBSMDBDIR!= cd ${.CURDIR}/../../lib/libsmdb; make -V .OBJDIR
+.endif
+LIBSMDB:= ${LIBSMDBDIR}/libsmdb.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+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..5ec92e5
--- /dev/null
+++ b/usr.sbin/edquota/edquota.8
@@ -0,0 +1,247 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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.
+.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 SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
+.Sh DIAGNOSTICS
+Various messages about inaccessible files; self-explanatory.
diff --git a/usr.sbin/edquota/edquota.c b/usr.sbin/edquota/edquota.c
new file mode 100644
index 0000000..87ef4df
--- /dev/null
+++ b/usr.sbin/edquota/edquota.c
@@ -0,0 +1,849 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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/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;
+ 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(0, 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()
+{
+ 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(name, quotatype)
+ 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(id, quotatype, fspath)
+ register long id;
+ int quotatype;
+ char *fspath;
+{
+ register struct fstab *fs;
+ register struct quotause *qup, *quptail;
+ struct quotause *quphead;
+ int qcmd, qupsize, fd;
+ char *qfpathname;
+ static int warned = 0;
+
+ setfsent();
+ quphead = (struct quotause *)0;
+ 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);
+ }
+ lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET);
+ 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(id, quotatype, quplist)
+ long id;
+ int quotatype;
+ struct quotause *quplist;
+{
+ register struct quotause *qup;
+ int qcmd, fd;
+
+ 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_WRONLY)) < 0) {
+ warn("%s", qup->qfname);
+ } else {
+ lseek(fd, (long)id * (long)sizeof (struct dqblk), 0);
+ 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(tmpf)
+ 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) {
+ register 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(quplist, outfd, name, quotatype)
+ struct quotause *quplist;
+ int outfd;
+ char *name;
+ int quotatype;
+{
+ register 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(quplist, inname)
+ struct quotause *quplist;
+ char *inname;
+{
+ register struct quotause *qup;
+ FILE *fd;
+ unsigned long bhardlimit, bsoftlimit, curblocks;
+ unsigned long ihardlimit, isoftlimit, curinodes;
+ int cnt;
+ register 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(quplist, outfd, quotatype)
+ struct quotause *quplist;
+ int outfd;
+ int quotatype;
+{
+ register 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(quplist, inname)
+ struct quotause *quplist;
+ char *inname;
+{
+ register struct quotause *qup;
+ FILE *fd;
+ int cnt;
+ register 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(secs)
+ 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(period, units, seconds)
+ 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(quplist)
+ struct quotause *quplist;
+{
+ register 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(s)
+ register const char *s;
+{
+ register 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(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(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;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
diff --git a/usr.sbin/edquota/pathnames.h b/usr.sbin/edquota/pathnames.h
new file mode 100644
index 0000000..92546f7
--- /dev/null
+++ b/usr.sbin/edquota/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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>
+
+#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..637e15a
--- /dev/null
+++ b/usr.sbin/eeprom/eeprom.8
@@ -0,0 +1,668 @@
+.\" 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 May 16, 2004
+.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 will use the system default MAC address.
+If set to
+.Dq Li true ,
+Ethernet devices which have a unique MAC address will use it rather
+than the system's default MAC address.
+Default:
+.Dq Li false .
+.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 scren 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 ofwdump 8
+.Sh HISTORY
+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 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..d04e61a
--- /dev/null
+++ b/usr.sbin/eeprom/ofw_options.c
@@ -0,0 +1,314 @@
+/*-
+ * 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)(struct ofwo_extabent *, int, const void *,
+ int, const char *);
+};
+
+static int ofwo_oemlogo(struct ofwo_extabent *, int, const void *, int,
+ const char *);
+static int ofwo_secmode(struct ofwo_extabent *, int, const void *, int,
+ const char *);
+static int ofwo_secpwd(struct ofwo_extabent *, int, const void *, int,
+ const char *);
+
+static struct ofwo_extabent ofwo_extab[] = {
+ { "oem-logo", ofwo_oemlogo },
+ { "security-mode", ofwo_secmode },
+ { "security-password", ofwo_secpwd },
+ { NULL, NULL }
+};
+
+static int ofwo_setpass(int);
+static __inline void ofwo_printprop(const char *, const char*, int);
+static int ofwo_setstr(int, const void *, int, const char *,
+ const char *);
+
+static int
+ofwo_oemlogo(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(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(struct ofwo_extabent *exent, int fd __unused,
+ const void *buf __unused, __unused 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 void
+ofwo_printprop(const char *prop, const char* buf, int buflen)
+{
+
+ printf("%s: %.*s\n", prop, buflen, buf);
+}
+
+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];
+ 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;
+ phandle_t optnode;
+ 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);
+ optnode = ofw_optnode(fd);
+ len = ofw_getprop_alloc(fd, optnode, 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/elf2exe/Makefile b/usr.sbin/elf2exe/Makefile
new file mode 100644
index 0000000..69e3622
--- /dev/null
+++ b/usr.sbin/elf2exe/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= elf2exe
+MAN= elf2exe.8
+MANSUBDIR= /${MACHINE_ARCH}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/elf2exe/elf2exe.8 b/usr.sbin/elf2exe/elf2exe.8
new file mode 100644
index 0000000..d41198c
--- /dev/null
+++ b/usr.sbin/elf2exe/elf2exe.8
@@ -0,0 +1,56 @@
+.\" Copyright (c) 1999 Stefan Esser
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 July 26, 1999
+.Dt ELF2EXE 8 Alpha
+.Os
+.Sh NAME
+.Nm elf2exe
+.Nd convert Alpha ELF executable to AlphaBIOS / ARCS format
+.Sh SYNOPSIS
+.Nm
+.Ar infile
+.Ar outfile
+.Sh DESCRIPTION
+The
+.Nm
+utility
+creates an executable that can be loaded by the AlphaBIOS or ARCS consoles
+as found on systems designed for
+.Tn Windows/NT .
+The input file must have been
+created as a non-relocatable standalone binary with a load address within
+the memory range available for user programs (0x80000000 to 0x806fdfff
+and 0x80900000 to at least 0x80ffffff).
+.Pp
+The command prints a list of sections found in the ELF executable and the
+section sizes and offsets of the output file for diagnostic purposes.
+.Pp
+Given an object file
+.Pa src.o
+the following two commands will create a binary for ARCS:
+.Bd -literal -offset indent
+ld -o a.out -M -N -Ttext 0x80900000 src.o
+elf2exe a.out a.exe
+.Ed
+.Sh BUGS
+The
+.Nm
+utility
+does not even attempt to verify that the input file matches the requirements
+for an ARC executable.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 4.0 .
diff --git a/usr.sbin/elf2exe/elf2exe.c b/usr.sbin/elf2exe/elf2exe.c
new file mode 100644
index 0000000..d0959eb
--- /dev/null
+++ b/usr.sbin/elf2exe/elf2exe.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 1999 Stefan Esser
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Make an ARC firmware executable from an ELF file.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/elf64.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define ALPHA_FMAGIC 0x184
+
+#define TOTHSZ 0x200
+
+typedef struct filehdr {
+ u_int16_t f_magic;
+ u_int16_t f_nscns;
+ u_int32_t f_timdat;
+ u_int32_t f_symptr;
+ u_int32_t f_nsyms;
+ u_int16_t f_opthdr;
+ u_int16_t f_flags;
+} FILHDR;
+#define FILHSZ 20
+
+#define ALPHA_AMAGIC 0407
+
+typedef struct aouthdr {
+ u_int16_t a_magic;
+ u_int16_t a_vstamp;
+ u_int32_t a_tsize;
+ u_int32_t a_dsize;
+ u_int32_t a_bsize;
+ u_int32_t a_entry;
+ u_int32_t a_text_start;
+ u_int32_t a_data_start;
+ u_int32_t a_bss_start;
+ u_int32_t a_gprmask;
+ u_int32_t a_cprmask[4];
+ u_int32_t a_gp_value;
+} AOUTHDR;
+#define AOUTSZ 56
+
+typedef struct scnhdr {
+ char s_name[8];
+ u_int32_t s_fill;
+ u_int32_t s_vaddr;
+ u_int32_t s_size;
+ u_int32_t s_scnptr;
+ u_int32_t s_relptr;
+ u_int32_t s_lnnoptr;
+ u_int16_t s_nreloc;
+ u_int16_t s_nlnno;
+ u_int32_t s_flags;
+} SCNHDR;
+#define SCNHSZ 40
+
+#define ROUNDUP(a,b) ((((a) -1) | ((b) -1)) +1)
+
+/*
+ * initialization subroutines
+ */
+
+int
+open_elffile(char *filename)
+{
+ int fileno = open(filename, O_RDONLY);
+
+ if (fileno < 0)
+ err(1, "%s", filename);
+ return (fileno);
+}
+
+
+Elf64_Ehdr *
+load_ehdr(int fileno)
+{
+ Elf64_Ehdr *ehdr;
+ size_t bytes = sizeof(*ehdr);
+
+ ehdr = malloc(bytes);
+ if (ehdr) {
+ lseek(fileno, 0, SEEK_SET);
+ if (read(fileno, ehdr, bytes) != bytes)
+ errx(1, "file truncated (ehdr)");
+ }
+ return (ehdr);
+}
+
+Elf64_Phdr *
+load_phdr(int fileno, Elf64_Ehdr *ehdr)
+{
+ size_t bytes = ehdr->e_phentsize * ehdr->e_phnum;
+ Elf64_Phdr *phdr = malloc(bytes);
+
+ if (phdr) {
+ lseek(fileno, ehdr->e_phoff, SEEK_SET);
+ if (read(fileno, phdr, bytes) != bytes)
+ errx(1, "file truncated (phdr)");
+ }
+ return (phdr);
+}
+
+Elf64_Shdr *
+load_shdr(int fileno, Elf64_Ehdr *ehdr)
+{
+ size_t bytes = ehdr->e_shentsize * ehdr->e_shnum;
+ Elf64_Shdr *shdr = malloc(bytes);
+
+ if (shdr) {
+ lseek(fileno, ehdr->e_shoff, SEEK_SET);
+ if (read(fileno, shdr, bytes) != bytes)
+ errx(1, "file truncated (shdr)");
+ }
+ return (shdr);
+}
+
+char *
+find_shstrtable(int fileno, int sections, Elf64_Shdr *shdr)
+{
+ size_t bytes;
+ char *shstrtab = NULL;
+ int i, shstrtabindex;
+
+ for (i = 0; shstrtab == NULL && i < sections; i++) {
+ if (shdr[i].sh_type == 3 && shdr[i].sh_flags == 0) {
+ shstrtabindex = i;
+
+ bytes = shdr[shstrtabindex].sh_size;
+ shstrtab = malloc(bytes);
+ if (shstrtab == NULL)
+ errx(1, "malloc failed");
+ lseek(fileno, shdr[shstrtabindex].sh_offset, SEEK_SET);
+ read(fileno, shstrtab, bytes);
+
+ if (strcmp (shstrtab + shdr[i].sh_name, ".shstrtab")) {
+ free(shstrtab);
+ shstrtab = NULL;
+ }
+ }
+ }
+ return (shstrtab);
+}
+
+int
+open_exefile(char *filename)
+{
+ int fileno = open(filename, O_RDWR | O_TRUNC | O_CREAT, 0666);
+
+ if (fileno < 0)
+ err(1, "%s", filename);
+ return (fileno);
+}
+
+/*
+ * utility subroutines
+ */
+
+static char *shstrtab;
+
+char *
+section_name(Elf64_Shdr *shdr, int i)
+{
+ return (shstrtab + shdr[i].sh_name);
+}
+
+int
+section_index(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i;
+
+ for (i = 0; i < sections; i++)
+ if (strcmp (name, section_name(shdr, i)) == 0)
+ return (i);
+ return (-1);
+}
+
+/* first byte of section */
+u_int64_t
+section_start(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+ return (shdr[i].sh_addr);
+}
+
+/* last byte of section */
+u_int64_t
+section_end(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+ return (shdr[i].sh_addr + shdr[i].sh_size -1);
+}
+
+/* last byte of section */
+u_int64_t
+section_size(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+
+ return (shdr[i].sh_size);
+}
+
+/* file position of section start */
+u_int64_t
+section_fpos(Elf64_Shdr *shdr, int sections, char *name)
+{
+ int i = section_index(shdr, sections, name);
+
+ if (i < 0)
+ return (-1);
+ return (shdr[i].sh_offset);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: elf2exe infile outfile\n");
+ exit(1);
+}
+
+int
+main(int argc, char** argv)
+{
+ int infd, outfd, i;
+ Elf64_Ehdr *ehdr;
+ Elf64_Phdr *phdr;
+ Elf64_Shdr *shdr;
+ FILHDR filehdr;
+ AOUTHDR aouthdr;
+ SCNHDR textscn, datascn;
+ u_int64_t textstart, textsize, textsize2, textfsize, textfpos;
+ u_int64_t datastart, datasize, datafsize, datafpos;
+ u_int64_t bssstart, bsssize;
+ u_int64_t progentry;
+ char* p;
+ int sections;
+
+ if (argc != 3)
+ usage();
+
+ infd = open_elffile(argv[1]);
+ ehdr = load_ehdr(infd);
+
+ if (ehdr == NULL)
+ errx(1, "cannot read Elf Header\n");
+
+ sections = ehdr->e_shnum;
+ progentry = ehdr->e_entry;
+
+ phdr = load_phdr(infd, ehdr);
+ shdr = load_shdr(infd, ehdr);
+ outfd = open_exefile(argv[2]);
+
+ shstrtab = find_shstrtable(infd, sections, shdr);
+
+ for (i = 1; i < sections; i++) {
+ printf("section %d (%s): "
+ "type=%x flags=0%llx "
+ "offs=%llx size=%llx addr=%llx\n",
+ i, shstrtab + shdr[i].sh_name,
+ shdr[i].sh_type, (long long)shdr[i].sh_flags,
+ (long long)shdr[i].sh_offset, (long long)shdr[i].sh_size,
+ (long long)shdr[i].sh_addr);
+ }
+
+ textstart = section_start(shdr, sections, ".text");
+ textsize = section_size(shdr, sections, ".text");
+ textsize2 = section_end(shdr, sections, ".rodata") - textstart +1;
+ if (textsize < textsize2)
+ textsize = textsize2;
+ textfsize = ROUNDUP(textsize, 512);
+ textfpos = section_fpos(shdr, sections, ".text");
+
+ datastart = section_start(shdr, sections, ".data");
+ datasize = section_start(shdr, sections, ".bss") - datastart;
+ datafsize = ROUNDUP(datasize, 512);
+ datafpos = section_fpos(shdr, sections, ".data");
+
+ bssstart = section_start(shdr, sections, ".bss");
+ bsssize = section_size(shdr, sections, ".bss");
+
+ printf("text: %llx(%llx) @%llx data: %llx(%llx) @%llx "
+ "bss: %llx(%llx)\n",
+ (long long)textstart, (long long)textsize, (long long)textfpos,
+ (long long)datastart, (long long)datasize, (long long)datafpos,
+ (long long)bssstart, (long long)bsssize);
+
+ memset(&filehdr, 0, sizeof filehdr);
+ memset(&aouthdr, 0, sizeof aouthdr);
+ memset(&textscn, 0, sizeof textscn);
+ memset(&datascn, 0, sizeof datascn);
+
+ filehdr.f_magic = ALPHA_FMAGIC;
+ filehdr.f_nscns = 2;
+ filehdr.f_timdat = time(0);
+ filehdr.f_symptr = 0;
+ filehdr.f_nsyms = 0;
+ filehdr.f_opthdr = AOUTSZ;
+ filehdr.f_flags = 0x010f;
+
+ aouthdr.a_magic = ALPHA_AMAGIC;
+ aouthdr.a_vstamp = 0x5004;
+ aouthdr.a_tsize = textfsize;
+ aouthdr.a_dsize = datafsize;
+ aouthdr.a_bsize = bsssize;
+ aouthdr.a_entry = progentry;
+ aouthdr.a_text_start = textstart;
+ aouthdr.a_data_start = datastart;
+ aouthdr.a_bss_start = bssstart;
+
+ strcpy(textscn.s_name, ".text");
+ textscn.s_fill = textsize;
+ textscn.s_vaddr = textstart;
+ textscn.s_size = textfsize;
+ textscn.s_scnptr = 0x200;
+ textscn.s_relptr = 0;
+ textscn.s_lnnoptr = 0;
+ textscn.s_nreloc = 0;
+ textscn.s_nlnno = 0;
+ textscn.s_flags = 0x20;
+
+ strcpy(datascn.s_name, ".data");
+ datascn.s_fill = datasize;
+ datascn.s_vaddr = datastart;
+ datascn.s_size = datafsize;
+ datascn.s_scnptr = 0x200 + textfsize;
+ datascn.s_relptr = 0;
+ datascn.s_lnnoptr = 0;
+ datascn.s_nreloc = 0;
+ datascn.s_nlnno = 0;
+ datascn.s_flags = 0x40;
+
+ write(outfd, &filehdr, FILHSZ);
+ write(outfd, &aouthdr, AOUTSZ);
+ write(outfd, &textscn, SCNHSZ);
+ write(outfd, &datascn, SCNHSZ);
+
+ lseek(outfd, textscn.s_scnptr, SEEK_SET);
+ p = malloc(ROUNDUP(textsize, 512));
+ if (p == NULL)
+ errx(1, "malloc failed");
+ memset(p, 0, ROUNDUP(textsize, 512));
+ lseek(infd, textfpos, SEEK_SET);
+ read(infd, p, textsize);
+ write(outfd, p, ROUNDUP(textsize, 512));
+ free(p);
+
+ lseek(outfd, datascn.s_scnptr, SEEK_SET);
+ p = malloc(ROUNDUP(datasize, 512));
+ if (p == NULL)
+ errx(1, "malloc failed");
+ memset(p, 0, ROUNDUP(datasize, 512));
+ lseek(infd, datafpos, SEEK_SET);
+ read(infd, p, datasize);
+ write(outfd, p, ROUNDUP(datasize, 512));
+ free(p);
+
+ return (0);
+}
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..0c65d7b
--- /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, 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..32dcbd0
--- /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..e50d7b5
--- /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 DIAGNOSTICS
+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..3df90a5
--- /dev/null
+++ b/usr.sbin/faithd/faithd.c
@@ -0,0 +1,907 @@
+/* $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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * User level translator from IPv6 to IPv4.
+ *
+ * Usage: faithd [<port> <progpath> <arg1(progname)> <arg2> ...]
+ * e.g. faithd telnet /usr/libexec/telnetd telnetd
+ */
+
+#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 __P((int, char **));
+static int inetd_main __P((int, char **));
+static int daemon_main __P((int, char **));
+static void play_service __P((int));
+static void play_child __P((int, struct sockaddr *));
+static int faith_prefix __P((struct sockaddr *));
+static int map6to4 __P((struct sockaddr_in6 *, struct sockaddr_in *));
+static void sig_child __P((int));
+static void sig_terminate __P((int));
+static void start_daemon __P((void));
+static void exit_stderr __P((const char *, ...))
+ __attribute__((__format__(__printf__, 1, 2)));
+static void grab_myaddrs __P((void));
+static void free_myaddrs __P((void));
+static void update_myaddrs __P((void));
+static void usage __P((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)
+{
+ 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)
+{
+ 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..f422432
--- /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 __P((int, int, const char *));
+extern void ftp_relay __P((int, int));
+extern int ftp_active __P((int, int, int *, int *));
+extern int ftp_passive __P((int, int, int *, int *));
+extern void exit_success __P((const char *, ...))
+ __attribute__((__format__(__printf__, 1, 2)));
+extern void exit_failure __P((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..6ceb24d
--- /dev/null
+++ b/usr.sbin/faithd/ftp.c
@@ -0,0 +1,1083 @@
+/* $KAME: ftp.c,v 1.23 2003/08/19 21:20:33 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 __P((void));
+static int ftp_passiveconn __P((void));
+static int ftp_copy __P((int, int));
+static int ftp_copyresult __P((int, int, enum state));
+static int ftp_copycommand __P((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);
+ return n;
+ }
+
+ memcpy(&data6, res->ai_addr, res->ai_addrlen);
+
+ 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..138c948
--- /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 __P((const char *, struct prefix *, int));
+static struct config *config_load1 __P((const char *));
+#if 0
+static void config_show1 __P((const struct config *));
+static void config_show __P((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..2a29639
--- /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 __P((const struct prefix *));
+extern int prefix_match __P((const struct prefix *, const struct sockaddr *));
+extern int config_load __P((const char *));
+extern const struct config *config_match __P((struct sockaddr *, struct sockaddr *));
diff --git a/usr.sbin/faithd/tcp.c b/usr.sbin/faithd/tcp.c
new file mode 100644
index 0000000..714df9a
--- /dev/null
+++ b/usr.sbin/faithd/tcp.c
@@ -0,0 +1,323 @@
+/* $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.
+ *
+ * $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 __P((int));
+static void sig_child __P((int));
+static void notify_inactive __P((void));
+static void notify_active __P((void));
+static void send_data __P((int, int, const char *, int));
+static void relay __P((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)
+{
+ /* 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)
+{
+ 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, 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..807e4a5
--- /dev/null
+++ b/usr.sbin/fdcontrol/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../fdread
+
+PROG= fdcontrol
+SRCS= fdcontrol.c fdutil.c
+CFLAGS+= -I${.CURDIR}/../fdread
+WARNS?= 2
+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..813956a
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.8
@@ -0,0 +1,335 @@
+.\"
+.\" 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..5ef5057
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.c
@@ -0,0 +1,218 @@
+/*
+ * 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 debug = -1, 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;
+
+ while((i = getopt(argc, argv, "d:Ff:s:v")) != -1)
+ switch(i) {
+ case 'd':
+ if (strcmp(optarg, "0") == 0)
+ debug = 0;
+ else if (strcmp(optarg, "1") == 0)
+ debug = 1;
+ else
+ usage();
+ show = 0;
+ break;
+
+ 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;
+mode = O_RDONLY | O_NONBLOCK;
+
+ 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 (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) {
+ 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 = ",";
+ }
+ 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);
+ }
+
+ if (debug != -1) {
+ if (ioctl(fd, FD_DEBUG, &debug) == -1)
+ err(EX_OSERR, "ioctl(FD_DEBUG)");
+ return (0);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/fdformat/Makefile b/usr.sbin/fdformat/Makefile
new file mode 100644
index 0000000..40ca9a7
--- /dev/null
+++ b/usr.sbin/fdformat/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../fdread
+
+PROG= fdformat
+SRCS= fdformat.c fdutil.c
+
+WARNS?= 2
+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..92dae57
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.1
@@ -0,0 +1,178 @@
+.\" 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.
+.Ar fill
+must be a number in the range 0 through 255 using common C
+language notation.
+The default value is
+.Dq Li 0xf5 .
+.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 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 DIAGNOSTICS
+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 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..dd92a86
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.c
@@ -0,0 +1,371 @@
+/*
+ * 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 (fdopts & FDOPT_AUTOSEL) {
+ if (ioctl(fd, FD_STYPE, &fdt) < 0)
+ err(EX_OSERR, "ioctl(FD_STYPE)");
+ } else if (fmtstring || format) {
+ errx(EX_USAGE,
+ "-f fmt or -s fmtstr is only allowed for autoselecting devices");
+ }
+ 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..e99c620
--- /dev/null
+++ b/usr.sbin/fdread/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= fdread
+SRCS= fdread.c fdutil.c
+
+.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..438a268
--- /dev/null
+++ b/usr.sbin/fdread/fdread.1
@@ -0,0 +1,222 @@
+.\"
+.\" 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 didn't 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 DIAGNOSTICS
+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).
+.Pp
+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..85336c2
--- /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_RDONLY)) == -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 (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 < 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 (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 (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 (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 (!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, status, 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 ((status = 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..a48eb17
--- /dev/null
+++ b/usr.sbin/fdread/fdutil.c
@@ -0,0 +1,530 @@
+/*
+ * 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) != 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];
+
+#ifdef PC98
+
+static struct fd_type fd_types_12m[] = {
+{ 15,2,0xFF,0x1B,80,2400,0,2,0x54,1,0,FL_MFM }, /* 1.2M */
+#if 0
+{ 10,2,0xFF,0x10,82,1640,1,2,0x30,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,1,2,0x30,1,0,FL_MFM }, /* 800K */
+#endif
+{ 9,2,0xFF,0x20,80,1440,1,2,0x50,1,0,FL_MFM }, /* 720K */
+{ 9,2,0xFF,0x20,40, 720,1,2,0x50,1,0,FL_MFM|FL_2STEP },/* 360K */
+{ 8,2,0xFF,0x2A,80,1280,1,2,0x50,1,0,FL_MFM }, /* 640K */
+{ 8,3,0xFF,0x35,77,1232,0,2,0x74,1,0,FL_MFM }, /* 1.23M 1024/sec */
+#if 0
+{ 8,3,0xFF,0x35,80,1280,0,2,0x74,1,0,FL_MFM }, /* 1.28M 1024/sec */
+#endif
+};
+
+static struct fd_type fd_types_144m[] = {
+#if 0
+{ 21,2,0xFF,0x04,82,3444,2,2,0x0C,2,0,FL_MFM }, /* 1.72M in 3mode */
+{ 18,2,0xFF,0x1B,82,2952,2,2,0x54,1,0,FL_MFM }, /* 1.48M in 3mode */
+#endif
+{ 18,2,0xFF,0x1B,80,2880,2,2,0x54,1,0,FL_MFM }, /* 1.44M in 3mode */
+{ 15,2,0xFF,0x1B,80,2400,0,2,0x54,1,0,FL_MFM }, /* 1.2M */
+#if 0
+{ 10,2,0xFF,0x10,82,1640,1,2,0x30,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,1,2,0x30,1,0,FL_MFM }, /* 800K */
+#endif
+{ 9,2,0xFF,0x20,80,1440,1,2,0x50,1,0,FL_MFM }, /* 720K */
+{ 9,2,0xFF,0x20,40, 720,1,2,0x50,1,0,FL_MFM|FL_2STEP },/* 360K */
+{ 8,2,0xFF,0x2A,80,1280,1,2,0x50,1,0,FL_MFM }, /* 640K */
+{ 8,3,0xFF,0x35,77,1232,0,2,0x74,1,0,FL_MFM }, /* 1.23M 1024/sec */
+#if 0
+{ 8,3,0xFF,0x35,80,1280,0,2,0x74,1,0,FL_MFM }, /* 1.28M 1024/sec */
+{ 9,3,0xFF,0x35,82,1476,0,2,0x47,1,0,FL_MFM }, /* 1.48M 1024/sec 9sec */
+{ 10,3,0xFF,0x1B,82,1640,2,2,0x54,1,0,FL_MFM }, /* 1.64M in 3mode - Reserve */
+#endif
+};
+
+#else /* PC98 */
+
+static struct fd_type fd_types_288m[] =
+{
+#if 0
+{ 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /*2.88M*/
+#endif
+{ 21,2,0xFF,0x04,82,3444,FDC_500KBPS,2,0x0C,2,0,FL_MFM }, /* 1.72M */
+{ 18,2,0xFF,0x1B,82,2952,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 10,2,0xFF,0x10,82,1640,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_144m[] =
+{
+{ 21,2,0xFF,0x04,82,3444,FDC_500KBPS,2,0x0C,2,0,FL_MFM }, /* 1.72M */
+{ 18,2,0xFF,0x1B,82,2952,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 10,2,0xFF,0x10,82,1640,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_12m[] =
+{
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 8,3,0xFF,0x35,77,1232,FDC_500KBPS,2,0x74,1,0,FL_MFM }, /* 1.23M */
+{ 18,2,0xFF,0x02,82,2952,FDC_500KBPS,2,0x02,2,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x02,80,2880,FDC_500KBPS,2,0x02,2,0,FL_MFM }, /* 1.44M */
+{ 10,2,0xFF,0x10,82,1640,FDC_300KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_300KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+{ 9,2,0xFF,0x23,40, 720,FDC_300KBPS,2,0x50,1,0,FL_MFM|FL_2STEP }, /* 360K */
+{ 8,2,0xFF,0x2A,80,1280,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 640K */
+};
+
+static struct fd_type fd_types_720k[] =
+{
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_360k[] =
+{
+{ 9,2,0xFF,0x2A,40, 720,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 360K */
+};
+
+#endif /* PC98 */
+
+/*
+ * 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 */
+
+#ifndef PC98
+ case FDT_360K:
+ case FDT_720K:
+ if (j == 250)
+ out->trans = FDC_250KBPS;
+ else
+ errx(EX_USAGE, "bad speed %d", j);
+ break;
+#endif
+
+ case FDT_12M:
+ if (j == 300)
+ out->trans = FDC_300KBPS;
+ else if (j == 500)
+ out->trans = FDC_500KBPS;
+ else
+ errx(EX_USAGE, "bad speed %d", j);
+ break;
+
+#ifndef PC98
+ case FDT_288M:
+ if (j == 1000)
+ out->trans = FDC_1MBPS;
+ /* FALLTHROUGH */
+#endif
+ 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, "+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");
+ 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);
+
+#ifndef PC98
+ 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;
+#endif
+
+ 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;
+
+#ifndef PC98
+ case FDT_288M:
+ fdtp = fd_types_288m;
+ n = sizeof fd_types_288m / sizeof(struct fd_type);
+ break;
+#endif
+ }
+
+ if (size == -1)
+ return fd_types_auto;
+
+ for (i = 0; i < n; i++, fdtp++)
+#ifdef PC98
+ if (((128 << fdtp->secsize) * fdtp->size / 1024) == size)
+ return (fdtp);
+#else
+ if (fdtp->size / 2 == size)
+ return (fdtp);
+#endif
+
+ 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;
+
+#ifndef PC98
+ case FDT_360K:
+ *name = "360K";
+ *descr = "5.25\" double-density";
+ break;
+#endif
+
+ case FDT_12M:
+ *name = "1.2M";
+ *descr = "5.25\" high-density";
+ break;
+
+#ifndef PC98
+ case FDT_720K:
+ *name = "720K";
+ *descr = "3.5\" double-density";
+ break;
+#endif
+
+ case FDT_144M:
+ *name = "1.44M";
+ *descr = "3.5\" high-density";
+ break;
+
+#ifndef PC98
+ case FDT_288M:
+ *name = "2.88M";
+ *descr = "3.5\" extra-density";
+ break;
+#endif
+ }
+}
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..b97bdd4
--- /dev/null
+++ b/usr.sbin/fdwrite/Makefile
@@ -0,0 +1,12 @@
+# ----------------------------------------------------------------------------
+# "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
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdwrite/fdwrite.1 b/usr.sbin/fdwrite/fdwrite.1
new file mode 100644
index 0000000..93de399
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.1
@@ -0,0 +1,127 @@
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "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
+Don't 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..b3ffb6e
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.c
@@ -0,0 +1,198 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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>
+
+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()
+{
+ 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;
+ char *device= "/dev/fd0", *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/fwcontrol/Makefile b/usr.sbin/fwcontrol/Makefile
new file mode 100644
index 0000000..54c24c1
--- /dev/null
+++ b/usr.sbin/fwcontrol/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= fwcontrol
+SRCS= fwcontrol.c fwcrom.c fwdv.c
+MAN= fwcontrol.8
+
+.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..b57e696
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwcontrol.8
@@ -0,0 +1,146 @@
+.\" 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 December 30, 2002
+.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 g Ar gap_count
+.Op Fl b Ar pri_req
+.Op Fl R Ar filename
+.Op Fl S Ar filename
+.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.
+.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 g Ar gap_count
+Broadcast
+.Ar gap_count
+by phy_config packet.
+.It Fl i Ar pri_req
+Set the
+.Dv PRIORITY_BUDGET
+register on all supported nodes.
+.It Fl R Ar filename
+Receive DV stream and dump it to a file.
+Use Ctrl-C to stop the receiving.
+Some DV cameras seem not to send the stream if a bus manager exits.
+If you cannot 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.
+.It Fl S Ar filename
+Send a DV file as isochronous stream.
+.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 stream.
+.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 .
+.Sh FILES
+.Bl -tag
+.It Pa /dev/fw0.0
+.El
+.Sh SEE ALSO
+.Xr firewire 4 ,
+.Xr fwe 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
+.Sh BUGS
+This utility is still under development and provided for debugging purposes.
diff --git a/usr.sbin/fwcontrol/fwcontrol.c b/usr.sbin/fwcontrol/fwcontrol.c
new file mode 100644
index 0000000..c250723
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwcontrol.c
@@ -0,0 +1,689 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/eui64.h>
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec13213.h>
+#include <dev/firewire/fwphyreg.h>
+
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern int dvrecv(int, char *, char, int);
+extern int dvsend(int, char *, char, int);
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "fwcontrol [-u bus_num] [-rt] [-g gap_count] [-o node] "
+ "[-b pri_req] [-c node] [-d node] [-l file] "
+ "[-R file] [-S file]\n"
+ "\t-u: specify bus number\n"
+ "\t-g: broadcast gap_count by phy_config packet\n"
+ "\t-o: send link-on packet to the node\n"
+ "\t-s: write RESET_START register on the node\n"
+ "\t-b: set PRIORITY_BUDGET register on all supported nodes\n"
+ "\t-c: read configuration ROM\n"
+ "\t-r: bus reset\n"
+ "\t-t: read topology map\n"
+ "\t-d: hex dump of configuration ROM\n"
+ "\t-l: load and parse hex dump file of configuration ROM\n"
+ "\t-R: Receive DV stream\n"
+ "\t-S: Send DV stream\n");
+ exit(0);
+}
+
+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 struct fw_devlstreq *
+get_dev(int fd)
+{
+ struct fw_devlstreq *data;
+
+ data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
+ if (data == NULL)
+ err(1, "malloc");
+ if( ioctl(fd, FW_GDEVLST, data) < 0) {
+ err(1, "ioctl");
+ }
+ return data;
+}
+
+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 = get_dev(fd);
+
+ 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;
+ goto gotnode;
+ }
+ }
+ if (i >= data->info_len)
+ 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];
+ int i;
+
+ data = get_dev(fd);
+ printf("%d devices (info_len=%d)\n", data->n, data->info_len);
+ printf("node EUI64 status\n");
+ for (i = 0; i < data->info_len; i++) {
+ devinfo = &data->dev[i];
+ fweui2eui64(&devinfo->eui, &eui);
+ eui64_ntoa(&eui, addr, sizeof(addr));
+ printf("%4d %s %6d\n",
+ (devinfo->status || i == 0) ? devinfo->dst : -1,
+ addr,
+ devinfo->status
+ );
+ }
+ free((void *)data);
+}
+
+static u_int32_t
+read_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int read, u_int32_t data)
+{
+ struct fw_asyreq *asyreq;
+ u_int32_t *qld, res;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
+ 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 (read)
+ 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 (!read)
+ asyreq->pkt.mode.wreqq.data = data;
+
+ if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
+ err(1, "ioctl");
+ }
+ res = qld[3];
+ free(asyreq);
+ if (read)
+ return ntohl(res);
+ else
+ return 0;
+}
+
+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);
+ 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 & 0x3f) << 24 | 1 << 23;
+ if (gap_count >= 0)
+ asyreq->pkt.mode.ld[1] |= 1 << 22 | (gap_count & 0x3f) << 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(1, "ioctl");
+ free(asyreq);
+}
+
+static void
+send_link_on(int fd, int node)
+{
+ struct fw_asyreq *asyreq;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
+ 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(1, "ioctl");
+ 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);
+ 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(1, "ioctl");
+ free(asyreq);
+}
+
+static void
+set_pri_req(int fd, int 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 = get_dev(fd);
+#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 && pri_req >= 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, int info_len)
+{
+ int i;
+ 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 = get_dev(fd);
+
+ 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(1, "ioctl");
+ }
+
+ return error;
+}
+
+static void
+show_crom(u_int32_t *crom_buf)
+{
+ int i;
+ struct crom_context cc;
+ char *desc, info[256];
+ static char *key_types = "ICLD";
+ struct csrreg *reg;
+ struct csrdirectory *dir;
+ struct csrhdr *hdr;
+ u_int16_t crc;
+
+ printf("first quad: 0x%08x ", *crom_buf);
+ hdr = (struct csrhdr *)crom_buf;
+ if (hdr->info_len == 1) {
+ /* minimum ROM */
+ struct csrreg *reg;
+ 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, hdr->info_len);
+
+ 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");
+ 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 char *port_status[] = {" ", "-", "P", "C"};
+ static char *pwr_class[] = {" 0W", "15W", "30W", "45W",
+ "-1W", "-2W", "-5W", "-9W"};
+ static char *speed[] = {"S100", "S200", "S400", "S800"};
+ tmap = malloc(sizeof(struct fw_topology_map));
+ if (tmap == NULL)
+ return;
+ if (ioctl(fd, FW_GTPMAP, tmap) < 0) {
+ err(1, "ioctl");
+ }
+ 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 %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.phy_delay,
+ 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(1, "ioctl");
+ 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(1, "ioctl");
+ 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 void
+open_dev(int *fd, char *devbase)
+{
+ char devname[256];
+ int i;
+
+ if (*fd < 0) {
+ for (i = 0; i < 4; i++) {
+ snprintf(devname, sizeof(devname), "%s.%d", devbase, i);
+ if ((*fd = open(devname, O_RDWR)) >= 0)
+ break;
+ }
+ if (*fd < 0)
+ err(1, "open");
+
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ u_int32_t crom_buf[1024/4];
+ char devbase[1024] = "/dev/fw0";
+ int fd, i, tmp, ch, len=1024;
+
+ fd = -1;
+
+ if (argc < 2) {
+ open_dev(&fd, devbase);
+ list_dev(fd);
+ }
+
+ while ((ch = getopt(argc, argv, "g:o:s:b:prtc:d:l:u:R:S:")) != -1)
+ switch(ch) {
+ case 'b':
+ tmp = strtol(optarg, NULL, 0);
+ open_dev(&fd, devbase);
+ set_pri_req(fd, tmp);
+ break;
+ case 'c':
+ open_dev(&fd, devbase);
+ tmp = str2node(fd, optarg);
+ get_crom(fd, tmp, crom_buf, len);
+ show_crom(crom_buf);
+ break;
+ case 'd':
+ open_dev(&fd, devbase);
+ tmp = str2node(fd, optarg);
+ get_crom(fd, tmp, crom_buf, len);
+ dump_crom(crom_buf);
+ break;
+ case 'g':
+ tmp = strtol(optarg, NULL, 0);
+ open_dev(&fd, devbase);
+ send_phy_config(fd, -1, tmp);
+ break;
+ case 'l':
+ load_crom(optarg, crom_buf);
+ show_crom(crom_buf);
+ break;
+ case 'o':
+ open_dev(&fd, devbase);
+ tmp = str2node(fd, optarg);
+ send_link_on(fd, tmp);
+ break;
+ case 'p':
+ open_dev(&fd, devbase);
+ dump_phy_registers(fd);
+ break;
+ case 'r':
+ open_dev(&fd, devbase);
+ if(ioctl(fd, FW_IBUSRST, &tmp) < 0)
+ err(1, "ioctl");
+ break;
+ case 's':
+ open_dev(&fd, devbase);
+ tmp = str2node(fd, optarg);
+ reset_start(fd, tmp);
+ break;
+ case 't':
+ open_dev(&fd, devbase);
+ show_topology_map(fd);
+ break;
+ case 'u':
+ tmp = strtol(optarg, NULL, 0);
+ snprintf(devbase, sizeof(devbase), "/dev/fw%d", tmp);
+ if (fd > 0) {
+ close(fd);
+ fd = -1;
+ }
+ if (argc == optind) {
+ open_dev(&fd, devbase);
+ list_dev(fd);
+ }
+ break;
+#define TAG (1<<6)
+#define CHANNEL 63
+ case 'R':
+ open_dev(&fd, devbase);
+ dvrecv(fd, optarg, TAG | CHANNEL, -1);
+ break;
+ case 'S':
+ open_dev(&fd, devbase);
+ dvsend(fd, optarg, TAG | CHANNEL, -1);
+ break;
+ default:
+ usage();
+ }
+ return 0;
+}
diff --git a/usr.sbin/fwcontrol/fwdv.c b/usr.sbin/fwcontrol/fwdv.c
new file mode 100644
index 0000000..43f5a98
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwdv.c
@@ -0,0 +1,410 @@
+/*
+ * 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 <dev/firewire/firewire.h>
+#include <dev/firewire/iec68113.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 8
+
+#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
+
+int
+dvrecv(int d, 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];
+
+ fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660);
+ buf = (char *)malloc(RBUFSIZE);
+ pad = (char *)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");
+ }
+
+ 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)\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;
+ printf("%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", k%10);
+#if FIX_FRAME
+ if (m > 0 && m != nb) {
+ /* padding bad frame */
+ npad = ((nb - m) % nb);
+ if (npad < 0)
+ npad += nb;
+ fprintf(stderr, "(%d blocks padded)",
+ 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++;
+ if (k % frame_rate[system] == 0) {
+ /* every second */
+ fprintf(stderr, "\n");
+ }
+ 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);
+ }
+ close(fd);
+ fprintf(stderr, "\n");
+ return 0;
+}
+
+
+int
+dvsend(int d, 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);
+ pbuf = (char *)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");
+ }
+
+ isoreq.ch = ich & 0x3f;
+ isoreq.tag = (ich >> 6) & 3;
+
+ if( ioctl(d, FW_STSTREAM, &isoreq) < 0)
+ err(1, "ioctl");
+
+ 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
+ printf("\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)\n");
+ fflush(stderr);
+ 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);
+ return 0;
+}
diff --git a/usr.sbin/getfmac/Makefile b/usr.sbin/getfmac/Makefile
new file mode 100644
index 0000000..4e532e6
--- /dev/null
+++ b/usr.sbin/getfmac/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= getfmac
+MAN= getfmac.8
+SRCS= getfmac.c
+
+WARNS?= 2
+
+.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..351f43e
--- /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
+
+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..962a3e7
--- /dev/null
+++ b/usr.sbin/getpmac/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= getpmac
+MAN= getpmac.8
+SRCS= getpmac.c
+
+WARNS?= 2
+
+.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..ee289e8
--- /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
+
+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/gstat/Makefile b/usr.sbin/gstat/Makefile
new file mode 100644
index 0000000..8cde0d7
--- /dev/null
+++ b/usr.sbin/gstat/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= gstat
+MAN= gstat.8
+SRCS= gstat.c
+WARNS?= 5
+DPADD= ${LIBGEOM} ${LIBDEVSTAT} ${LIBKVM} ${LIBBSDXML} ${LIBCURSES} ${LIBSBUF}
+LDADD= -lgeom -ldevstat -lkvm -lbsdxml -lcurses -lsbuf
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/gstat/gstat.8 b/usr.sbin/gstat/gstat.8
new file mode 100644
index 0000000..5d48266
--- /dev/null
+++ b/usr.sbin/gstat/gstat.8
@@ -0,0 +1,90 @@
+.\" 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 3, 2003
+.Dt GSTAT 8
+.Os
+.Sh NAME
+.Nm gstat
+.Nd print statistics about GEOM disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl cd
+.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 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 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 DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr geom 4
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.Fx 5.0 .
+.Sh BUGS
+The
+.Nm
+utility only works interactively.
+It should probably detect when it is run non-interactively and produce
+simple
+.Tn ASCII
+text output.
+Otherwise, this utility should probably be folded into
+.Xr systat 1
+which only works interactively and is the obvious utility to run for
+various other forms of system statistics.
diff --git a/usr.sbin/gstat/gstat.c b/usr.sbin/gstat/gstat.c
new file mode 100644
index 0000000..c92f70e
--- /dev/null
+++ b/usr.sbin/gstat/gstat.c
@@ -0,0 +1,266 @@
+/*-
+ * 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 <string.h>
+#include <paths.h>
+#include <curses.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <libgeom.h>
+#include <sys/resource.h>
+#include <devstat.h>
+#include <sys/devicestat.h>
+
+static int flag_c, flag_d;
+static int flag_I = 500000;
+
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int error, i, quit;
+ 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;
+ short cf, cb;
+ char *p;
+ long double ld[11];
+ uint64_t u64;
+
+ while ((i = getopt(argc, argv, "dcI:")) != -1) {
+ switch (i) {
+ case 'c':
+ flag_c = 1;
+ break;
+ case 'd':
+ flag_d = 1;
+ 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()");
+ 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);
+ 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);
+ printw("dT: %5.3f flag_I %dus sizeof %d i %d\n",
+ dt, flag_I, sizeof(*gsp), i);
+ printw(" L(q) ops/s ");
+ printw(" r/s kBps ms/r ");
+ printw(" w/s kBps ms/w ");
+ if (flag_d)
+ printw(" d/s kBps ms/d ");
+ printw("%%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 != NULL && gid->lg_what == ISCONSUMER &&
+ !flag_c)
+ continue;
+ if (gsp->sequence0 != gsp->sequence1) {
+ printw("*\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);
+
+ printw(" %4ju", (uintmax_t)u64);
+ printw(" %6.0f", (double)ld[0]);
+ printw(" %6.0f", (double)ld[1]);
+ printw(" %6.0f", (double)ld[2] * 1024);
+ printw(" %6.1f", (double)ld[3]);
+ printw(" %6.0f", (double)ld[4]);
+ printw(" %6.0f", (double)ld[5] * 1024);
+ printw(" %6.1f", (double)ld[6]);
+
+ if (flag_d) {
+ printw(" %6.0f", (double)ld[8]);
+ printw(" %6.0f", (double)ld[9] * 1024);
+ printw(" %6.1f", (double)ld[10]);
+ }
+
+ if (ld[7] > 80)
+ i = 3;
+ else if (ld[7] > 50)
+ i = 2;
+ else
+ i = 1;
+ attron(COLOR_PAIR(i));
+ printw(" %6.1lf", (double)ld[7]);
+ attroff(COLOR_PAIR(i));
+ printw("|");
+ if (gid == NULL) {
+ printw(" ??");
+ } else if (gid->lg_what == ISPROVIDER) {
+ pp = gid->lg_ptr;
+ printw(" %s", pp->lg_name);
+ } else if (gid->lg_what == ISCONSUMER) {
+ cp = gid->lg_ptr;
+ printw(" %s/%s/%s",
+ cp->lg_geom->lg_class->lg_name,
+ cp->lg_geom->lg_name,
+ cp->lg_provider->lg_name);
+ }
+ clrtoeol();
+ printw("\n");
+ *gsq = *gsp;
+ }
+ geom_stats_snapshot_free(sp);
+ clrtobot();
+ refresh();
+ usleep(flag_I);
+ i = getch();
+ 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 'q':
+ quit = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ endwin();
+ exit (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: gstat [-cd] [-I interval]\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/i4b/Makefile b/usr.sbin/i4b/Makefile
new file mode 100644
index 0000000..01f7642
--- /dev/null
+++ b/usr.sbin/i4b/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+SUBDIR= dtmfdecode g711conv isdnd isdndebug isdndecode isdnmonitor isdnphone \
+ isdntel isdntelctl isdntest isdntrace man
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.sbin/i4b/Makefile.inc b/usr.sbin/i4b/Makefile.inc
new file mode 100644
index 0000000..1195419
--- /dev/null
+++ b/usr.sbin/i4b/Makefile.inc
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+# if you don't like curses stuff in the daemon (i.e. don't intend
+# to ever run it in the foreground but are using the monitoring
+# utilities instead) define this to compile it without.
+#I4B_WITHOUT_CURSES = 1
+
+# if you would like monitoring support, define this
+I4B_EXTERNAL_MONITOR = 1
+
+# for the security conscious type: restrict monitoring to the
+# local machine by not compiling any tcp/ip support for monitoring
+# at all
+#I4B_NOTCPIP_MONITOR = 1
+
+.include "${.CURDIR}/../../Makefile.inc"
diff --git a/usr.sbin/i4b/dtmfdecode/Makefile b/usr.sbin/i4b/dtmfdecode/Makefile
new file mode 100644
index 0000000..74a9df7
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/Makefile
@@ -0,0 +1,19 @@
+#---------------------------------------------------------------------------
+#
+# $FreeBSD$
+#
+# last edit-date: [Tue Dec 14 21:17:46 1999]
+#
+#---------------------------------------------------------------------------
+
+PROG= dtmfdecode
+
+CFLAGS+= -DDEBUG
+
+# libm is only necessary if USE_COS is defined in the source
+#LDADD= -lm
+
+test: ${PROG}
+ uudecode -p < dtmfsounds.al.uu | ./${PROG}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/dtmfdecode/dtmfdecode.1 b/usr.sbin/i4b/dtmfdecode/dtmfdecode.1
new file mode 100644
index 0000000..c1e296c
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfdecode.1
@@ -0,0 +1,69 @@
+.\"
+.\" Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: dtmfdecode.1,v 1.6 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:53:13 1999]
+.\"
+.\"
+.Dd February 15, 1999
+.Dt DTMFDECODE 1
+.Os
+.Sh NAME
+.Nm dtmfdecode
+.Nd decodes DTMF tones from A-law audio data
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is part of the isdn4bsd package and is used to detect DTMF tones in the
+audio stream.
+.Pp
+It reads audio G.711 A-Law coded data from stdin and outputs the detected
+numbers values as ASCII characters to stdout.
+.Pp
+The detector is implemented as 8 narrow band-pass filters realized with
+an integer double-cross recursive algorithm. Various ad-hoc methods are
+employed to provide hysteresis and anti-bounce for the detected signals.
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+dtmfdecode < beep.al
+.Ed
+.Pp
+will print a "1" to stdout.
+.Sh STANDARDS
+ITU Recommendations G.711
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Poul-Henning Kamp Aq phk@FreeBSD.org .
+This man page was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/dtmfdecode/dtmfdecode.c b/usr.sbin/i4b/dtmfdecode/dtmfdecode.c
new file mode 100644
index 0000000..16c68e6
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfdecode.c
@@ -0,0 +1,152 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: dtmfdecode.c,v 1.6 1999/12/13 21:25:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * Extract DTMF signalling from ISDN4BSD A-law coded audio data
+ *
+ * A-Law to linear conversion from the sox package.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+/* Integer math scaling factor */
+#define FSC (1<<12)
+
+/* Alaw parameters */
+#define SIGN_BIT (0x80) /* Sign bit for an A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static int
+alaw2linear(a_val)
+ unsigned char a_val;
+{
+ int t;
+ int seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#ifdef USE_COS
+/* The frequencies we're trying to detect */
+static int dtmf[8] = {697, 770, 852, 941, 1209, 1336, 1477, 1633};
+#else
+/* precalculated: p1[kk] = (-cos(2 * 3.141592 * dtmf[kk] / 8000.0) * FSC) */
+static int p1[8] = {-3497, -3369, -3212, -3027, -2384, -2040, -1635, -1164};
+#endif
+
+/* This is the Q of the filter (pole radius) */
+#define POLRAD .99
+
+#define P2 ((int)(POLRAD*POLRAD*FSC))
+
+int
+main(int argc, char **argv)
+{
+ int i, kk, t, nn, s, so, ia;
+ int x, c, d, f, h[8], k[8], n, y[8];
+#ifdef USE_COS
+ int p1[8];
+#endif
+ int alaw[256];
+ char key[256];
+
+ for (kk = 0; kk < 8; kk++) {
+ y[kk] = h[kk] = k[kk] = 0;
+#ifdef USE_COS
+ p1[kk] = (-cos(2 * 3.141592 * dtmf[kk] / 8000.0) * FSC);
+#endif
+ }
+
+ for (i = 0; i < 256; i++) {
+ key[i] = '?';
+ alaw[i] = alaw2linear(i) / (32768/FSC);
+ }
+
+ /* We encode the tones in 8 bits, translate those to symbol */
+ key[0x00] = '\0';
+
+ key[0x11] = '1'; key[0x12] = '4'; key[0x14] = '7'; key[0x18] = '*';
+ key[0x21] = '2'; key[0x22] = '5'; key[0x24] = '8'; key[0x28] = '0';
+ key[0x41] = '3'; key[0x42] = '6'; key[0x44] = '9'; key[0x48] = '#';
+ key[0x81] = 'A'; key[0x82] = 'B'; key[0x84] = 'C'; key[0x88] = 'D';
+
+ nn = 0;
+ ia = 0;
+ so = 0;
+ t = 0;
+ while ((i = getchar()) != EOF)
+ {
+ t++;
+
+ /* Convert to our format */
+ x = alaw[i];
+
+ /* Input amplitude */
+ if (x > 0)
+ ia += (x - ia) / 128;
+ else
+ ia += (-x - ia) / 128;
+
+ /* For each tone */
+ s = 0;
+ for(kk = 0; kk < 8; kk++) {
+
+ /* Turn the crank */
+ c = (P2 * (x - k[kk])) / FSC;
+ d = x + c;
+ f = (p1[kk] * (d - h[kk])) / FSC;
+ n = x - k[kk] - c;
+ k[kk] = h[kk] + f;
+ h[kk] = f + d;
+
+ /* Detect and Average */
+ if (n > 0)
+ y[kk] += (n - y[kk]) / 64;
+ else
+ y[kk] += (-n - y[kk]) / 64;
+
+ /* Threshold */
+ if (y[kk] > FSC/10 && y[kk] > ia)
+ s |= 1 << kk;
+ }
+
+ /* Hysteresis and noise supressor */
+ if (s != so) {
+/* printf("x %d %x -> %x\n",t,so, s); */
+ nn = 0;
+ so = s;
+ } else if (nn++ == 520 && key[s]) {
+ putchar(key[s]);
+/* printf(" %d %x\n",t,s); */
+ }
+ }
+ putchar('\n');
+ return (0);
+}
diff --git a/usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu b/usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu
new file mode 100644
index 0000000..f4e0c07
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfsounds.al.uu
@@ -0,0 +1,2098 @@
+$FreeBSD$
+begin 666 dtmfsounds.al
+M5555U=74U-564-34UU%5U-?1UM'7UM37U=5555145%545%155%555%555=75
+M5575U=75U-15U5555575U575U=75U=75U=34U-35U-35U=75U=55U=5555%0
+MU-374535U-;7T=?6U]?4U-75U555551555555555555555555%5555145=75
+MU-34U-34U-35U=75U=75U=55U=75U=55555555755=5545/4U-=15-37T=;1
+MU];7U]34U=5555555%145%145%555555U555U=55U=34U=75U=75U-75U=35
+MU-74U=74U-75U=75U=75U=55U=75U=564-34UU!4U-36U]'6UM?7U-35U555
+M55555555555455155555555555545%55U-74U-34U-34U=74U-35U=35U=75
+MU=75U=75U=75U=75U590U-374534U-'6T=;6U]?5U-555%145%155%545555
+M5555U555U=75U=74U-5555755=75U=74U=75U=75U=35U=34U=74U-35U=75
+M555545/4U-=15-34T=;1U];7U]34U=55555555555%555%545%555=555575
+M5535U=34U-34U-34U-34U-75U=555555555555555555U=75U=514]34UU%4
+MU-?1UM'6UM?6U-15U51555155514551455555=75U=75U=75U=35U=75U=75
+MU=75U-34U-34U-75U=75U=75U=74U=75U=5555%0U-374535U-'6T=;1UM;4
+MU-75555555145%555555555555555575U5555=75U-34U-34U-74U=75U-74
+MU=75U=5555555=55U=75U=755E#4U-=15-34T=;1UM;7U]34U=5555555514
+M5%555%5555155575U=74U=74U55555555=75U-35U-34U-75U=75U-75U=75
+M5555555555104]74UU%5U-;0UM#6UM?7U=35U-755554551455155%155575
+MU=75U=155U755=75U-34U-34U-34U-75U575U555U=55U5555575U=75U5%3
+MU-375E74U]'6T=?6U]?4U-755=5555145%5555555555U=55U555U575U-55
+MU=75U=75U=34U=35U=75U=75U-75U=74U-75U=75U=7545/4U-=15-74UM36
+MU];7U]34U=75U5555%5455555555555555555%5555=4U=74U-34U]34U-35
+MU=75U=75U555555555555555U555U5564]34UU94U-?1U]'7UM37U-35U=75
+M555555145%5455155575U=75U=75U=?4U=75U=75U=75U=75U=75U-35U-75
+MU=75U=35U=75U=7555%3U-?74534U-;7UM?6U]?7U-35U555555555545%14
+M55555575U=75U5545%55U=74U]?7U-34U-35U=75U=75U=75U=75U=75U=75
+MU=5545/5U-965=?7T=;1U];4U]75U5555555555555155%5555555=75U=35
+MU=74U-75U=75U-74U=75U=75U=35U=35U-74U=75U=75555555514]37UE95
+MU-31UM'7UM?7U-35U-5555555%545%145%155575U575U=345%75U=34U-34
+MU-34U=75U=75U=75U=75555555555=55U575U5%3U-374534U]'6T=;1U]?4
+MU-755554551555555555555555555=75U575U-35U=75U=35U-75U-75U-34
+MU=75U=75U=75U=75U=75U55545/5U-=15-74UM;6U];4U]34U=5555555555
+M55555%1555555=55U=75U5145575U=77U-?7U]34U-35U=75U=75U=55U=55
+MU=75U=75U=564]34UU%4U=36U];7U]37U=35U=75555555555%555%555%55
+M5=75U=75U=34U=55U=75U=75U-75U=74U=74U-35U=75U-34U-75U57555%3
+MU=774534U-'6T=;6U];4U-74U555551555545514555555555=75U=155-75
+MU-34U-34U-35U=75U=75U=755=5555555=75U=755=5545#5U-=15=37T=;1
+MU];7U]74U=5555155%555%155%145575U=75U=75U=74U-35U=75U=35U=75
+MU=74U=75U=75U=74U=75U=55U555U5514]75UU%4U=31U]'7UM?7U-35U555
+M5%1455155%55555555555=75U=355515U=75U-34U-37U-35U=75U=35U-75
+M555555555575U=75U590U-375E77U]'6T=;1U]?4U-755555555555555514
+M555455155555U=55U-35U=75U-74U-74U-74U=75U=75U=35U=75U=75U=55
+MU55545+5U=115-37T=;0UM;7UM37U-15U5555%5455555%555%555=75U575
+MU%54U=75U-75U-34U-35U=75U=74U=74U=75U575U=75U=75U=514-34UU%4
+MU-?1UM'6UM?7U-35U5755555551555145515555555755=75U=34U=75U575
+MU555U=55U=75U=75U=75U=34U-75U=75U=75U590U-?65E37U-'6T=?6U];4
+MU]3555545%145U145%1455555575U=55U=555-75U-34U-37U-34U-34U=35
+MU=75U=75U555555555555=755E#4U]965=37T-;1UM;4U]745=55U5555514
+M5%1455155=755=75U=75U=74U]75U=75U=75U=75U=75U=75U=74U-35U-35
+MU=75U555U5514]75UU%4U-31UM'7UM?7U-?4U-7555555%145%=455555575
+MU=755=755%15U=74U-34U-34U-34U-75U=75U=55U=55U=55U=75U=75U590
+MU]365E77U];6T=?7U=35U5755555555555555%5555555=55U555U=75U-?5
+MU=75U-75U-35U-34U=75U=75U=75U=75U=555=55U=7545/5U-=65=37T=;1
+MU]'7UM34U=7555555%1455555%555555U=755=75U5545=75U-34U-34U-75
+MU=75U=75U=755=75U=55U=75U=75U=564-37UE95U-31UM'6UM?7U-35U=75
+M55555%155%14551555555555U=75U=37U]35U=35U=755=75U=74U-35U-75
+MU=75U=75U=75U=75U5%3U=374534U-;7UM?6U-?4U]345=5555545%545555
+M55555555U555U=555%55U=74U-34U]34U-35U=75U=75U=75U=75U=75U=75
+MU=7545/5U-=15-37T='0UM'7U]34U=35U5555%145%14555455555555U=74
+MU-74U]35U5755555U575U=74U-34U-75U=75U=74U=74U=74U-164]34UU%4
+MU-31U]'6T=?7U-35U=755=75555555545%15551555555575U515U-34U-34
+MU-35U-34U-34U-75U55555555555U=75U=75U590U-364534U-;6T=;1U];7
+MU-7555555555555555555%5555755555U555U=34U=75U-74U-35U=35U-74
+MU-74U-35U=75U=75U=75U=755E#4U-=15-34UM?1U]'7UM?7U-3555555%55
+M55555%155%1555555575U=545=34U-?7U-34U-35U=755=75U=75U=35U=75
+MU=75U=74U=564-37UE94U-31UM'7UM37U-35U%5555545%145%555%555575
+MU=75U=75U=74U]75U=75U-74U=74U=75U=75U=75U=75U=35U=75U=75U5%3
+MU=374534U-'6T=?6U-?4U-7555555%145%145%145%55U575U-75U=355%75
+MU=75U=35U=34U-35U-35U=75U-75U=75U=75U=75U=755E/4U-965=37T=;1
+MUM;7U]74U=75555555545%=45%145%155575U=75U=75U-?5U=75U=34U=75
+MU-?5U-15U]=5U=55U-?4U=355=555%534-54UM57U]'4UM#4U]37U577UE+4
+MU432V5?%4U]40-!5U]I4U%-0U=77UE;3UU/64-'24M'45]W55]'1UU574%'5
+MUM34TU14UU!75E'7UM%5U=!77E=4T=+3W];65%]15U70T=S15%105U16UE!6
+MT]7655Q55%?1U-957UU7V<?;TE]#6=39S=A564-15MS:5]944-95U-=65E%4
+MT=#3UU)45U97U];1UM34U=;0T-564577T-;75%%04577U]?555=75U35U-34
+M55175%555]375=?6T]/755155=75U]15U-37U]545%145=74U=174]75U5%4
+MU-?6U-?4UM#0UM?5U5545=545%975555U=545%75U=755575U=36UM75U555
+M5=7455555=74U]?4U-75U]74U-74U-75U57555%0U=774%15U=;7UM;6U];4
+MU-355=35U=5555175%54557555145=755=355U75U=34U=34U-35U-75U-34
+MU]35U=75U=7555755=75U=555E/5U-=15-37T]'3T=;7U-74U=75U5545%54
+M5555U=755%=45%555%55U575U-?555555=75U=74U-74U-?7U=755=755=55
+MU=75U-34U5514]37UE%4U-?1UM#7UM?7U]?5U=7555555%14555555555%55
+M5=75U=75U515U=74U-37U]34U=755=75U=35U=35U=75U=75U=75U=74U%90
+MU=374577U]'1T-;1UM?4U-755=75U5545%545=55555555555%555=55U=34
+MU=555=75U-35U=75U-74U=74U=35U=74U=74U=75U=755E/5U-=15-34T='1
+MUM'7UM37U=75U555555555145U=45%555=7555555=545-75U-37U]34U-34
+MU-74U=75U-355575U=555=75U=35U=114-34UE%5U]31U]'7T=;4U-15U-55
+MU55555=75555U51555555=75U=35U=74U]355=555=755575U=34U-34U-75
+MU-75U=75U=75U=55U5%3U-375E34U]'6T=?1U]?4U]34U555U5555%145%15
+M55555555U=75U=755535U=37U-34U-35U-35U-75U=55U=75U=75U=75U=34
+MU-755E#4U-=15-37T=;1UM;7U]34U=555555555455155555555455555=75
+MU=75U-;7U-545=36U%35U];65U345U355=35T-?4UE-4U5#0T=154]35U5!7
+MU]71UM37UM745%545=37UM9555=75U35U=34U%575U975577U]355575U=?7
+MUM;7U]34U%555574U=555-75U=35U=74U575U%=1U-774577UM/1T-;7U=55
+MU5755575U5555U145%555=555=55U=75U-35U=74U-75U=75U-74U=74U-74
+MU=74U=75U=34U-?4U=55555545/4U-=15-37T=;1UM;4UM34U=35U5545%14
+M5%15555555555=55U=75U=755%75U=34U-37U-?4U-34U=755=7555555=55
+M5=55U=75U5514M74UU95U-?1UM#6UM?7U=75U-7555545%15555555555555
+MU=75U=74U575U-75U=755=75U=75U=75U=75U=34U-74U=34U=35U=7555!3
+M5=774%?5U]#0T=?6U-?7U]745=7555155554U554U5155555U=75U=3455=5
+MU=34U=34U-34U-74U575U=75U=75U575U=555=75U=5545/5U-915=37T-;0
+MUM'4U]74U554U555555555145%1455555575U555U=74U]?55-?6U=;75=17
+M5];4UM94T=14UU!4U%'7U%?3U%31U==17=57U%=5UM31T-76T]70U=745M75
+M5M945]=75=17U%17U595UU37U%7555545-37U-37U]?4U=34U-34U]545%35
+MU575UM1555155=75U%=3U=?65E35U-?7UM31U];7U-755=555%15U5145514
+M5%35U=75U5555-74U=37U]155=75U=77U]34U-75U=75U=75U=345575U-35
+MU]?55E/5U-105-74UM?1UM'7UM?7U-35U5555%175%555%555555U=75U=75
+MU=355=75U=35U-34U-34U-35U-75U5555=55U5755575U=75U5514M74UE95
+MU-31UM'7T=?7U-35U575555555555%545%145=75U=35U=74U=75U-35U575
+M5575U=74U-34U-35U=75U575U=75U=75U=7555%0U=374574U]'6T-?1U-?4
+MU=755=555555555555555%55555555555=75U515U=74U-34U-34U=34U-35
+MU-75U=75U55555155=75U=755E/4U-965-?4T=;1UM'7UM34U=5555155%14
+M555555555=5555555555U=75U=?7U-35U-75U-35U=35U-75U=35U=75U=75
+MU=75557555514E74UU%4U-?1UM'6T=?6U]?4U=7555155%15555555555554
+M5575U=75U=545-75U=34U-?4U-34U-34U=75U=75U=75U=75U=75U=7555%2
+MU=774537U]'6T-;1U]?4U-755=5555555%545%145%555575U=75U=75U=74
+MU]35U=35U-75U=75U=75U=75U=74U-74U=34U=75U55545/5U]965=34UM;1
+MU];4U]34U=75U5555554555555555555U575U=75U=755%35U=34U]?7U-34
+MU=75U=75U=75U=75U=555=75U=35U=514]34UE95U]?1UM'6T=?7U-?4U-75
+M555555145%145%155555U=75U=74U575U-?4U=75U=75U=55U=75U=75U=34
+MU-74U=34U-35U=75U5%3U=?65E74U]'6T=?7U-?5U-75U555U55555555555
+M555555755=75U=75U5545=75U-34U]?7U]34U=75U=75U=755=555=75U=75
+MU=7545/4U]975=?4T=;1U];4U]34U=75U555555555155555555555555=75
+MU=35U=37U-75U=75U=75U=75U=74U=34U-75U=34U-35U=35U5504E74UU%4
+MU-31UM'6T=?7U=34U=75555555555%54555555555575U=75U=155=75U=35
+MU-34U-34U-35U-75U=75U=75U=75U=75U=75U5%3U=365U77U]'6T=;6U-?4
+MU=75555555555%555%555555U=75U=75U=34U=74U]355=755=75U=75U=75
+MU=75U=75U=74U-34U-35U=7545+5U-965=37T=?1U]?7U]745=75U5555554
+M5%14551455155=75U-34U-34U575U=74U-34U-34U-34U=75U=7555555=55
+MU=75U=55U5104E74UU95U]?1UM#1UM?4U=35U5555555555555145%155575
+MU=75U=35U-55U-35U=75U=75U=75U-75U=75U=75U=34U-34U-34U=7555%2
+MU=375E74U]'6T=?6U-?5U-75U=55U5555554555455555575U=75U=74U554
+M5=75U-34U-34U-34U-75U=75U=75U=75U=75U575U=5545+5U-975=?7T=;1
+MUM;4U]74U=75U5555%1455145555555555555555U=75U=37U-75U=75U=75
+MU=34U=34U-35U-75U=75U=74U=5555504E75UE95U-31UM'6T=?7U-34U-75
+M555555155555555555555555U555U=555%75U=34U-37U-34U=74U-75U=75
+MU=55U=75U=75U=75U593U=?65E74U]'6T=?6U-?4U-755555551555145555
+M5555U=75U=75U=75U575U]35U=75U=75U=75U=75U-34U=75U=75U=74U-35
+MU=355E/5U-965=?7T=;1UM;7U]74U=5555545%145%1455155%555575U=75
+MU=35U555U-34U-34U-34U-74U=35U=75U=75U=75U=75U=75U5514E74UE95
+MU-?1UM'6T=?6U-35U555555555155%5455145%155=75U=75U=75U=?4U=75
+MU=75U=75U=75U=34U=75U=34U-75U-34U=75U%93U-?15U77U]#1T-;6U]?4
+MU-35U55555555%145%155%145%555575U=75U5545=74U-34U]?4U]34U]34
+MU-34U=75U=75U575U575U57545+5U-965=37T=;1U];4U]34U55555545%55
+M55555555U55555755=74U=35U=77U]75U=75U=74U=74U=74U=75U=75U=75
+MU-34U-34U=564]77UE95U-?1UM'7UM37U-35U=755=555%55555555555575
+M55555=55U5755574U-?7U]34U-34U=75U=75U=75U=7555555%155=75U5%3
+MU=365E77U]#6T=;6U]?4U-55555555145%1455555555U5755=755=755575
+MU-34U-74U=34U-35U-35U-75U=75U=35U=74U-75U=755E/5U-965=37T=;1
+MUM;7U-34U555U555551455145%145%545555U=75U=74U%75U-74U-34U=34
+MU=35U-74U=75U=75U=74U=74U=74U-564]37UE95U-?1UM'7UM;7U-35U555
+M5%145%155%55551555555=75U=34U-35U=?7U-35U-75U=75U=75U-74U=75
+MU=75U=74U-35U=74U593U=?15U77U]'7T=?6U-35U-755555555555545%54
+M555555555=75U=75U=555=75U-34U]?7U]?7U]34U=755=55U575U=55U555
+MU57545+5U-965=?7T-;0UM;4U]755555551555555=5555155555U=75U=75
+MU-755574U]75U=75U=75U=74U-74U=75U=75U=74U-34U-35U=564]74UU95
+MU-36UM'7T=?7U-35U575U=5455155%145%155%555=55U=75U=755574U-34
+MU=34U-34U-34U=35U=75U=55U575U575U=755514U=745=75U=35U=34U-34
+MU-75U=75U=74U-75U5755=55U=75U=75U=755555U=35U=755=5555555575
+MU=75U-75U-34U-34U-35U=755%15U-15U=74U-34U-75U=755555U=555=75
+MU5755=75U=75U=74U=35U-75U5545575U=75U=55U=75U=75U-74U=75U575
+MU=74U-74U-555-74U%75U=34U-34U-34U-7555555555U=75U=55U=75U=75
+MU=555=75U=75U=37U=555555U5555=75U-74U-34U-74U=74U=74U=75U554
+MU=355575U=34U-34U-34U-35U=5555555=75U555U=75U=75U=55U5555554
+M5-75U=34U-34U=35U-34U-74U=55U=55U555555555755%35U-15U=75U-74
+MU-34U-35U-75U=7555555=555555U=75U=55U=75U5555574U]35U=555=55
+M5575U=75U=75U575U=755=75555555545-75U%75U-74U=35U=34U=75U-34
+MU-35U=35U=75U=75U5555575U=7555755515U=74U=75U-74U=75U=75U=75
+MU=75U=35U=75U=75U555U=745=74U-35U-74U=75U=55U5555575U=75U=75
+MU=75U=75U=74U=35U555U=34U=75U-35U-75U=75U=75U=75U=74U-74U-35
+MU=755535U=15U=75U-34U=75U=74U=34U=34U=75U=755575U575U=75U=34
+MU55555175555U=75U-35U-34U-35U-75U=75U=74U=75U=75U=555-74U%75
+MU-34U]35U=75U=55U5555555U=75U5555555U=75U=34U-75U=75U=37U]75
+MU=75U=755=75U=75U=75U=74U-34U-34U=5555175=745=74U-34U-34U=75
+MU=555=555575U555U575U=75U=74U-34U-34U5555%35U=37U-?4U]?4U-35
+MU=5555145%155575U=74U-34U574U-15U=74U]37U-35U5555575U=75U=75
+MU-75U55555145575U=75U-75U=74U]?5U-755=55U=75U=75U-34U-34U-15
+MU=75U=75U=355=35U%15U=74U=34U]34U-34U-75U=75U575U=75U=74U=34
+MU=555%145%145%=4U=34U]34U]35U-55U575U=74U=35U=55U555U=75U%55
+MU-375=75U=35U-74U-?4U-35U=75555555555575U=75U=75U=35U-35U=35
+MU=34U=75U=75U=75U=75U=75U=74U=75U=75U-34U5755534U-35U=35U-34
+MU=75U=75U=35U-75U=75U5755=5555555=55U=74U=75U5545=55U-34U-35
+MU-35U=75U555U=75U=75U=74U-35U=545]75U%75U-34U-34U-35U=75U575
+MU=75U=5555555575U=75U=74U-75U575U=74U-555575U=75U575U=75U-34
+MU-35U=74U=74U=75U5545=745=74U-?4U]34U-75U=755=55U=75U5555=75
+MU=75U=75U555U=5555555%35U=34U=74U-74U-75U-75U=75U=75U=755=75
+MU=355%35U=15U=74U-74U-34U-35U=75U5555=75U=75U=75U=75U=55U575
+M5575U=75U-?5U=75U=75U-35U-74U=75U=75U=75U=34U=75U=545-75U%75
+MU=74U=34U-75U=75U=75U=75U=75U=75U-75U=75U=755=55551555=5U=74
+MU-34U-35U=35U=75U=75U=35U=75U=755555U514U-345=74U-34U=34U=75
+MU=75U=5555555575U=75U=75U-75U=75U=75U=35U-?7U-75U=755=755=75
+MU=35U=75U=75U555U=74U-755574U-=55=75U-74U=35U-34U=75U5555555
+M5575U575U=75U5555-74U=55U=555=74U-75U-35U=35U=34U-75U=755575
+MU=755575U=555=75U%75U-74U-?4U]34U=75U=75555555555555U575U=75
+MU=75U=75U=75U=74U]5555555=75U=75U=35U-75U-35U=75U=74U-75U555
+MU=755%55U=75U-35U-35U-34U-74U-74U=75U=75U=75U=75U=75U=755=75
+M5535U=75U=75U=75U=75U=74U-34U=755575U=75U=755575U=155=75U=74
+MU-35U]34U-75U-74U574U=74U=34U=35U=75U=75U=55U575U=?5U=755555
+M5555U5555=75U-75U=34U-34U-74U=555-75U515U=74U=75U-75U=75U=75
+MU5555=555575U=75U=555=755=75U=75U%55U=75U-75U-74U=74U=75U=75
+MU=75U-55U555U575U5545=745-74U-37U]?7U-35U=75U=75U=555555U555
+M5575U=74U=74U-35U=755=77U=75U=34U-75U=75U575U=75U=35U-75U=75
+MU=755574U-?5U=34U-34U=75U=75U=75U=75U=55U5555575U=75U575U=74
+MU-75U=155-75U=34U-34U-74U=75U=5555555=55U=555=55U=555-75U515
+MU=74U-37U-75U=75U=75U=75U=75U=55U=75U=75U=75U=75U=75U574U]75
+M55555%555=75U=74U=35U=75U=75U=74U-75U=54U=345575U=34U-34U=75
+MU-75U575U55555555555U=75U=75U=75U=755=755535U=34U-34U-74U=75
+MU=555555U=75U=75U575U=74U574U-155=74U=34U-35U=34U-75U=75U=75
+M55755=75U=75U=75U=75U=75U=55U=35U55555555555U=75U-74U-35U-75
+MU=75U-34U=355=74U515U=74U=34U-35U=75U=75U-75U=555575U=75U=55
+M55555=75U=75U=545=75U-35U-35U-35U=74U=74U=755=5555555=55U555
+MU-345575U=35U-34U=34U-75U=75U=75U=75U=74U=75U=55U55555555=55
+M5=77U-75U555557555755=75U=75U=75U=75U=35U=755574U-55U=75U-74
+MU=35U=75U=75U-75U-35U=75U57555555=75U=75U55555555U75U=35U-35
+MU-74U=35U=35U=74U=74U-75U-75U%555=74U%74U-?4U-34U=755=75U=55
+M5=55U575U57555555575U=34U-35U=75U=75U]?5U=35U=75U555U=75U=75
+MU=35U=34U=74U=75U555U-345574U=34U-74U=35U=75U=75U=75U=75U=75
+MU=755=555575U=755=7555=45=74U-74U-75U=75U575U=75U=75U=55U575
+MU=755575U=545=74U-34U-?4U-75U=55U55555555=75U-34U-34U-34U=75
+MU=555575U=34U555551555555575U=75U=34U=34U-34U-74U=555=35U575
+MU=34U-34U-34U=35U=75U=755=7555555=55U57555555=755555U=545=75
+MU=75U-34U-35U=3555555=555=75U=75U574U555U=755%75U=35U-34U-35
+MU=75U=35U=75U=75U=75U=55U=55U555U-555555U574U%55555555755=75
+M5555U575U=74U-74U=34U=74U577U-15U=75U-34U-?4U-75U=75U=74U=75
+MU=75U=75U=75U=555575U5555=755%15U=74U-34U-35U-75U=755=75U=35
+MU=75U=75U=555=3555=5U=74U-36U-34U-75U5755=75U=7555555575U=75
+MU=35U=755=75U=55U-?555555=75U-74U-75U5555=75U=34U-34U-74U575
+MU-355%55U=74U-74U=35U=74U=74U-75U=75U=755=75U=75U=75U=755575
+M55145=75U=35U=35U=755=75U575U=74U-75U=5555555%75U555U=74U]34
+MU=34U-35U=75U=55U5755=75U555U=75U=55U575U-755=555=3755555555
+M5=755555U555U=34U]34U-34U-75U=555=55U514U=34U-34U%75U=75U=74
+MU-35U=55557555755=55U=55U=34U=75U=545=75U-?4U-34U-35U=75U=75
+MU555U=75U=75U=75U555U=755%75U=34U-35U-34U=75U=555555U575U=75
+MU=75U575U=75U=555555U=74U]355=55U=55U=74U-34U=75U=75U=75U=34
+MU=355=77U-545=75U=35U=35U-35U-75U575U=75U=75U555U5755575U-75
+MU555U=74U535U=74U-34U=74U=74U-34U=75U=755=75U=75U=555=35U515
+MU=74U-34U]75U=75U=755=75U575U=34U=75U575U555U=75U=75U=75U-35
+MU555555555555=75U=74U-74U-74U-34U-35U=55U-355=55U=55U=755=74
+MU=34U-34U-75U575U=75U=55U555U5555=755575U515U-34U-34U=75U-75
+MU=75U=34U=35U=75U=75U5555574U-54U555U-75U-34U-34U-34U-75U=75
+MU5555%555575U=74U-34U=75U=55U=36U]34U575U5755555U=75U=34U-74
+MU-34U-35U=555=34U%35U=75U=75U=35U-35U-35U=75U575U5555575U575
+MU%75U=755=55U=55U=35U-34U-74U=74U-74U-75U-55U575U5755515U575
+MU-345=55U=34U-37U-35U-55U=75U=75U=7555555=75U=75U=7555555575
+MU=75U]1555755575U575U=75U575U=74U=74U=75U=155=75U=545575U-34
+MU-35U-35U=75U=35U=355=55555555555=55U=75U535U=75U575U=74U-34
+MU-74U-74U=75U=755=55U=35U-55U-555=35U555U=74U=34U-34U-75U=55
+M5=555=555=55U=55U555U555U=75U=75U=75U-35U=755%755=55U=155555
+M5=75U=755=55U=755575U-755%155=75U-37U-?4U=74U=75U=5555555=75
+MU=75U=75U=75U-74U-75U-545=35U=35U=75U=75U-75U=75U=75U=555554
+M5=355574U%545575U-74U-34U-34U-?4U-34U=55557555555575U=74U=75
+MU=75U=355=77U-55U555U575U-75U575U-77U=745575U=555=155-35U515
+MU-75U]34U-75U=75U=75U=74U=75U=55U=35U555U=55U=75U=55U=145=75
+MU-75U-34U-35U%75U-74U=74U=74U=75U=35U=55U=545%35U=34U-34U=34
+MU=155575U=55U=55U=355=755=75U=75U=?5U=35U=55U]?5U575U=74U=74
+M5=345=75U=75U-75U-74U-34U574U-545%75U-75U-75U=74U=74U-35U=35
+MU=75U=75U=75U575U=75U=355=745554U=35U-74U]35U-75U=75U=755=74
+MU=755555U=155=35U575U=74U]34U-35U=75U=75U=55U-75U=7555555555
+MU=55U=75U=75U=75U=34U55555555=75U=55U=75U=75U]34U-75U-34U575
+MU-555%75U-35U-34U-75U=34U=75U555U515U=55U5555=755=75U-74U574
+M5=555=55U-75U-35U-55U5755=755=555=555=15U=55U535U-575575U=77
+MU-74U-35U=755=745575U=55U=75U=555=555=75U=7555555=37U]?5U555
+MU515U=755=74U575U-75U=35U=15U=55U=34U%75U-74U=34U-35U-35U=75
+M5575U5155=75U=35U-5555155574U=77U-?45=75U=75U=75U=34U-37U-74
+MU=75U=5555555575U555U-345-755=74U=75U-35U=745=74U-745=75U=75
+MU=75U=74U=755=755=755575U]?5U=5555555575U-55U=755-775-76U=74
+M5=155=34U==55%755=77U=75U-355=75U=74U-34U=75U555U=74U5755=74
+MU-35U=75U5545=75U-355=?5U=35U-75U575U=75U=34U=755=555=75U535
+MU-34U-34U=75U=355=75U555U-35U-75U=55U555U555U=35U-55U-75U-37
+MU5555555U555U=75U-75U-35U=755555U=755=555=75U=55U-34U]74U575
+MU=75U-75U5555=555=155=55U=55U-55U-155=?6U=54U==5U-15U=75UM?4
+MU537U5755=545-=7U-94U-55U=555=145%74U-35U=?4U-745=555=74U-74
+MU=34U-545517U575U%74U]37U=34U=74U%555%75U=35U=555=34U=15U-35
+MU]75U%75U%755=755535U-76U-34U=355575U555U515U577U=745-745=74
+M5%755=35U=355=755%34U-35U=35U-74U=74U=75U-74U-35U=75U555U514
+MU=755=75U=35U-34U-35U-75U=75U55555555575U=75U=55U=75U5755%15
+M5=75U-?4U=35U=7555555575U5555575U575U575U=755%75U=545=75U-75
+MU-75U-35U-34U-35U=755=55U575U=75U=75U=755=755%1555=45575U=75
+MU-34U-34U=34U=34U-3555755575U=755=75U5755=755=75U=75U=755=75
+M5=75U=555=55U=355=35U=35U-34U%55U=75U=37U-35U=5555555=55U575
+MU575U=74U=34U=15U554U=74U=75U=?4U-355=155=75U=74U-755555U555
+M5575U=555=75U-75U555U=555%75U-74U-34U=75557555755=75U=75U=75
+MU-35U=75U=15U=34U-75U=35U575U57555755=75U=75U=34U=55U=75U=75
+MU=55U=75U=74UM35U=555=75U=75U-75U=75U=74U-35U=75U=555=74U-75
+MU=34U-?4U=155=155-555=75U=75U=55U=55U=75U=75U57555555575U515
+M5=34U-?4U-?4U=555=755=35U=75U=55U=7555755-765-77U=74U577U534
+MU%34U%75U%74U57555555575U=55U=75U=355=555=55U-?5U-75U555U=55
+MU=75U=75U=35U=75U=74U=355555U=15U=34U-?4U=35U=74U=74U=745575
+MU5545=55U=75U=75U=74U-555%75U=55U-35U=75U=35U-35U-75U-55U-74
+MU-75U5555%555-75U575U575U=35U-75U=35U=75U=7555755555U=35U=75
+MU575U=75U5545555U=74U]55U=55U5555=75U=75U=75U=35U=75U-75U%55
+MU=345=74U=75U-75U=75U-74U-75U-75U=555=555=75U=75U555U=555515
+M5=555=74U-?7U]34U-34U-75U=75U=755=5555555=555515U=75U=74U-34
+MU=34U=75U=74U=75U=55U5555=75U=75U=75U=74U=555%15U=75U]34U575
+M55755575U=74U-35U-35U-55U=55U=555=75U-75U-34U=35U=75U=74U=34
+MU=75U=555555U=555575U=75U=34U%545=75U535U-37U-34U=35U=75U=74
+MU=75U575U=555555U555U-74U=74U-34U-34U-35U=75U=74U=75U=74U=75
+M5=75U=755555U-355575U=75U=345=755=75U=75U=75U=55U=75U=75U=35
+MU=345%75U=155=75U=34U-35U-75U=75U=755555U555U=75U=75U=55U=74
+MU-555U15U=555=75U-35U-34U-35U=74U=74U=75U=75U=75U=555=75U-74
+MU-37U]?4U-35U=75U=555555555555355555U=75U=75U=?7U=545=75U=77
+MU]74U=75U575U=755=75U=75U=75U=75U=35U5555=74U=75U-34U-355575
+MU-755=75U-75U555U=555=555=755=75U-?45%355=555=74U-?7U-34U=75
+MU=75U=75U55455555=75U=755535U=555=74U]?7U-34U]34U-75U555U=75
+MU%75555555555=75U=34U]755%15U=75U]?4U=35U=75U=75U=75U=35U-75
+MU=75U=75U-555-74U-75U=75U=34U-74U-75U575U=75U=75U575U575U=55
+M5=75U=37U5175%75U555U-34U-37U-34U-755=555=35U=75U575U555U554
+MU5755=75U=75U=75U=74U=75U=755555U5755=75U=75U=75U=75U-?45U=5
+M5=755=?4U=55U555U=75U=75U-75U=35U=75U=75U-355574U-155=74U-77
+MU-35U=35U=75U=34U=75U=75U=75U=755=555=74U]545U555=155=35U=35
+MU-35U-75U-34U-74U=75U=75U=75U5555%75U575U-74U-34U-34U-74U555
+M555555755575U575U=75U=75U=?7U5575575U=74U-75U=75U=75U=75U=55
+MU=75U=75U=74U-75U=55U=74U574U=34U-34U=35U=75U=35U=74U=75U=75
+MU-75U=55U575U-355%145=555575U=34U-34U-74U-74U-35U=75U-74U-34
+MU=755535U=35U=34U]?7U-35U=5555555555U=555=75U=75U=75U=75U=74
+MU]545U15U=75U];4U-75U=755=75U=75U=35U=75U=75U=75U=555%75U%55
+MU=37U-34U-?55=75U=75U=5555555555U=75U=75U=74U-?7U5175%15U555
+MU-34U]?4U-35U=75U=75U=75U=75U575U=75U555U=755=74U=35U=75U=34
+MU-75U-74U=75U=7555155575U=75U=74U]?45%=455555=77U-75U=75U=75
+MU=75U-75U=35U=75U=75U=755575U-35U=34U]34U=75U=755575U=555=75
+M55555=75U=75U=55U=77U]575U15U=755-77U]355=37U-145-75U-75U=37
+MU-355%!6T]'4UE95T]75U5%4U]?5U5=5U]155515U-155%15U=555=74U-35
+M5=74U-;6U%145%755=77UM?4U=35U=555=74U=35U=34U-75U=75U-35U=55
+MU=74U=74U-55U=74U-75U-74U=74U=755-74U=555=34U=555=75U-145%17
+M5%55U-75U]34U]74U]75U-34U-75U=55U=755575U=555%?5U-155=37UM34
+MU=75U-75U=77U-755575U=755=75U=755%75U=34U-545U545=75U]?4U]?4
+MU%75U-355555U-55U=75U-55U-75U]55U=75UU75U-75U-74U%74U=74U=74
+MU-34U=75U=555515U575U575U-?7U5165%55U557U=74U-34U]34U-355555
+M5=555575U5555515U514U=74U=74U=75U-34U=35U-75U-75U=75U555U=75
+M5575U=75U=75U]?55%=45=75U=?7U-155=55U=155=75U%55U=75U=75U=75
+MU=755555U=35U=34U-75U-355=74U=755-74U5555=75U=555=75U=755=77
+MU]575U145=555=75U]35U-75U-35U-75U-75U=74U-75U=55U=555-75U-75
+MU-34U=35U-35U-34U=7555555%555=75U=55U-55U555U-?7U%175%55U=74
+MUM?4U-75U=75U515U=75U%54U=75U=55U=7555175=345575U-?5U=34U=15
+M5=55U-35U-75U-74U574U57555555=74U]?55%=75%75U575U-77U=34U=75
+MU-35U-55U-55U554U%55U575U535U-35U=75U-74U-34U-75U=74U-75U=74
+MU%54U575U515U-74U%77UM545E=55575U=34U=75U575U555U555U=55U=74
+MU-75U-74U-555575U]74U=76U-77U-775=755%755%755=755=755=155=15
+MU=54U=?7UU565%15U5565-35U-15U-?4U]15U-?5U-14U=55U=54U=75U554
+M5=74U=34U-35U-74U-74U-74U57455755=755%55U=555=15U-35U];455=6
+M5%555=34U]34U=35U=355=75U=75U=34U=355=75U-355515U-75U=74U]?4
+MU=55U-35U=75U-34U=75U575U575U5555=555-77U]545U1455755%77U-?7
+MU=74U-75U=55U=75U575U=75U=75U5555%74U-75U=34U-35U=75U-35U-75
+MU-55U=755555U=755=755=75U=3455175%55U=74UM;4U-745=74U=75U=35
+MU=34U=55U=75U=34U%54U=745575U=74U=34U-35U-35U=75U=75U=75U=75
+MU=755=75U=35U-?55%=45-75U514U=75U]34U-34U-74U575U57555755=75
+M5=755515U=555=75U-34U-35U-75U=55U5755555U=75U=75U5755=75U=74
+MU]545E145555U=34U-75U575U-75U%75U-74U%75U=74U574U=745=74U-74
+MU=74U=34U-75U=75U=755575U=74U=35U=35U=35U=35U-?7U5165%=55=55
+M5=75U-?4U-?4U-34U-75U=75U-75U575U55555145=745=74U-34U-34U=34
+MU-35U=75U=75U=75U=75U=55U=555=75U-?55%=55-75U=74U]75U=55U555
+M5575U=75U-74U-34U-35U=745575U=35U-35U-34U=35U=75U=755555U=75
+MU=5555555575U=75U=74U]545U55U=355574U=34U-34U-34U=75U=75U=55
+MU555555555555575U%75U=74U=35U=75U=35U=75U=75U=555=75U=755=75
+MU=75U-?7U5175%35U=75U-?4U=75U=75U-75U=35U-74U-35U=75U=75U=55
+MU=345=75U=35U=75U=75U=75U-34U=74U-75U=75U=75U=75U=75U-?55U=4
+M5%75U555U=74U-34U-75U-75U=35U=34U=75U=75U=755575U=15U=75U-34
+MU=35U-75U=75U=75U-555=755=555=75U=75U=77U]545E145=555=37U=75
+MU=55U=555=75U=75U=75U=75U=75U=555-75U575U-34U-34U-75U=75U=74
+MU=75U=75U=75U=55U=75U=75U=?7U5175%55U=555-75U-34U-34U-34U-34
+MU=75U-75U=74U-34U=75U=355575U=75U-34U-75U=34U=75U=755=555555
+M55755575U=75U-?55%=45-75U=74U]35U=55U=75U575U=75U=75U555U=75
+MU-34U574U-15U-34U-74U=75U=75U=75U=75U=75U=35U-34U=75U5555=77
+MU]545E145575U575U-34U-34U-34U-35U=75U575U=55U5555=555-75U575
+MU-34U=34U-34U=35U-75U55555555575U=75U=55U555U=?7U5175%155575
+MU=34U=75U=75U=75U=75U-75U=75U=75U=74U-55U-755575U=34U-34U-75
+MU=75U=75U=75U=75U=75U=35U=755=75U-?55%=45%75U554U=34U-35U-34
+MU-34U=74U=75U=55U55555555%35U=555=75U-34U=35U=75U=35U=75U=55
+M5555U555U=75U=75U=74U]545U145=75U=77U]75U=75U=34U=34U=75U=35
+MU=35U=75U=555=75U-75U-34U-35U-35U-35U-7555755=755=75U=555=75
+MU=75U=?7U%175555U=555-75U=34U-34U-34U-74U-75U=75U=75U=34U555
+MU=34U=75U=35U=75U=75U=75U-75U=55U575U=75U=755=75U=75U-?55%=4
+M5%74U=74U]34U=555=75U=75U=75U575U5755=75U=74U575U=55U=74U-34
+MU-35U-75U=75U=75U=75U575U=75U=75U=75U=74U]575U145=74U575U-37
+MU-34U-75U-75U=55U5555555U=7555555-75U5555=74U=35U-34U=75U=35
+MU=75U=55U5555=7555555=75U=34U5175575U=75U=?4U=755=75U=555=75
+MU=75U=75U=75U=75U-55U=345=75U=35U-34U-34U-75U=75U-74U-75U-34
+MU-75U=75U575U-?55%=45-75U=545=75U-35U-35U-35U-75U-75U-74U-75
+MU=755575U=35U=75U-35U-35U=75U555U=75U=75U=74U=75U=755575U=77
+MU]545U145=75U=77U-34U-75U=74U=74U=34U=75U=75U-75U=555%55U555
+MU=74U=35U=75U=75U=74U=34U=75U=75U=75U=75U=75U=?7U5175515U=75
+M5=74U-34U-34U=75U=75U=75U=75U=755=755555U=755=75U=35U-75U=75
+MU=74U=75U5555=755555U5555=75U=75U-355%=45%755=75U-?5U=75U=75
+MU=75U=75U=75U=75U=55U=755575U=55U=74U-34U-34U-34U=35U=75U=55
+MU=5555555=55U=75U=77U]545U55U=74U555U=74U-37U-34U=34U=75U=75
+MU=55U575U5555-75U515U=74U=35U-34U=35U=755575U=35U=7555555554
+M5575U=37U%145575U=75U=37U=75U=75U=75U=75U=75U-35U-74U=75U=55
+MU=755=75U=34U-34U=75U=75U=34U-35U-35U-75U575U575U=74U]?55%=4
+M5%75U=155=35U-35U=75U=75U=75U555U=75U=55U=755575U=555=75U-34
+MU-34U-35U=75U=75U=55U5555=55U=75U=75U=74U]545U145=75U=37U]75
+MU=35U=75U=75U=35U=75U=75U=75U=75U=75U-75U-34U-34U-75U=74U-75
+MU574U=75U=75U=75U-75U=75U=?7U5575555U=7555755=74U=34U-74U=75
+MU=75U-75U=35U=74U555U=34U=34U=35U-35U=35U=55U555U555U=75U=75
+M5555U=55U=75U-?455=45555U=75U]?4U-34U=75U=75U=75U=75U=55U=74
+MU=74U=75U=55U=74U]74U-35U=34U=75U=35U=35U=75U=35U-75U=35U-37
+MUM155%555575U514U=75U-35U-34U-74U=35U=555=75U=75U=555=75U515
+MU=75U=75U=34U=75U=75U=75U=75U=75U575557555555=34U5175%555=75
+MU=37U=75U555U555U=75U=75U=75U-75U=34U-55U=755-75U=35U=74U=55
+MU=75U-35U-74U-35U-75U=75U=75U=75U]?55%=45555U=545-75U-34U-34
+MU-35U=75U=55U=75U=75U5755535U=555=74U-34U-34U=75U=75U=75U=75
+MU575U=75U555U=75U=77U]155%55U=74U=74U]34U-755=755575U=35U=75
+MU=74U=35U-755=75U575U=75U=34U-34U-75U=7555555575U5755575U=55
+MU=75U=34U5=75U155=34U-74U]37U-34U-75U=75U=75U=75U57555555514
+M55755%75U=34U-34U=75U=75U=75U=75U5555=75U=75U=555575U-355%94
+M5575U-75U];4U=755=75U=75U=755=55555555555=75U535U=55U=34U-74
+MU=75U=75U=55U5555=555555U=75U=75U-75U=74U]575D!F9'!Q1EK4T-IC
+M)"`_,0T4^)B-L[.UBK>VNJ6]CXFTM)(6$H6RLUP^)C]FB)D/.SLW!`$*"Q&2
+MZ@PX.P6VN[4&/#;DM[:%4N:'AW0`&X"XNX0W.0F/N++D#@]ME_X1%)RTCQX\
+M/QVQNXH#/C!XCXSD$7V1Z!HT#I*\OY`P.C6!O['.#@UFE=,=:H6VB!T\/Q"S
+MNK4&/#9TC(+_%<69E`0V")*_OI$P.C6'O+;9#`-Y[WX:%(>QBAP_/A.PNK4&
+M/3=*@H;`;N:$E@8Q"YV^OI,P.C6'LK?=`@=/X&X&%H&SM1P_/A*PNK4',C1)
+M@85<8I6`D0`S-9V^N9,P.S6%L[37`1O?Y!$#$8.RM!P^/AVPN[4$,S5-AYQQ
+M9Y.-DP(R-)VYN9(Q.PJ:L(I1!Q+S^1\,$X*]MQP^.1RQNXH%,0M$FI1F>)B.
+MG0T]-)VYN9TQ.`N8L8E>!6KA\`4)'8R_MQPY.1RVN(L:-@Y2GN!I<(2+GP\\
+M-YVYN9PV.0B>MXQ`'F7KRP8*'(^^MATY.1^VN8@8-PW5D\P72H"TF0X_-I*Y
+MN9PV/@F<M(-+$U*1Q@,U'HZYMATY.1^WOHD9-0#9EW421H*WFPD^-I*YN9PW
+M/PZ2BH9Q%_^<WPPW&(FYL1(Y.1ZWOX\?"P?"[F(>7(^VA0@Y,9.YN9\W/`^0
+MB85_;^F;UPXV!8BXL!,Y/AZTO(P=#AKWYQ<%5XBPA`LX,9"YN9\T/0V1C)]E
+M9)"$5@LQ!8N[L!,^/AZUO8(2#1S_]AP'T+6SAPHX,9&^N9\U,@*7@)%F=)B!
+M7C4P!(J[L!`^/AZ*LX`0`!?DTP4!W+2RA@H[,):^OI\U,`"4A^]B58>"7C0R
+M!XJZLQ$^/QZ(L(81!V+@308"V+>\@34[,)2_OIP+,0;JFO)I]8.,63<]!HJZ
+MLQ8_/!Z.MH06&W?O?@(,V[:_@34[,)6_OYT)-@3HG5!J^HR)6#8\`8JZLQ<\
+M/!^/MYH4'<&58`\.V+&^@#4Z,.J\O)T.-!KNEG@7XHF+63$_`(JZLQ0\/1R-
+MM9\5%.:7:0@+WK"^@S4Z,.F]O9,,"A_L[&D1Z[6U7C`_`XJZLVH],AV#B)!K
+M8Y20%#4UW;"Y@C4Z,.RRLI`""1#M_Q`2E[2T73`^`HJZLV@R,Q*!CY5H<9V=
+M$30TT+.XC0H[,.*SLY$`#!7BT1D<D+:W5C,Y#8NZLVDS,!.&@N)NW9J9$C<W
+MU;*XC0H[,."PL)8&`&/C<`09DK&VU3,X#8NZL&\P,1&$AO]L^8::'#$V4+*[
+MC`L[,.>QL94'!WW@8P$:G["QTC(X#(B[L&TQ-A>:FMQM[X*$&3`Q1;*[C`@X
+M,>6VM^@%&%'@%0T$F;*PV#(X#(FXL6,V-!6>G71BEX^'&S,P0+VZCP@X,?ZW
+MM.P8$_#@$PX'F[VSPS([#XFXL6$W-6F=E&9BG8B!!3(P3KVZCPDY-OVTBN$<
+M:N/@'@L!A;RRR#T[#XZYMF8T"&*0X6IBF+6#!3TS=KVZCPX^-_&*B?L3993C
+M!34`A;^]]ST[#XR^MV<*#V>7PA!MA+>-!#PR<KVZCP\_-_>(C?867)+B!S<"
+MA+^]\CT[#XV_MV4(`GSK2AYMAK:,!#\]>+VZCPP\-/6/@-IJ_9OM`38-A+Z\
+M_C([#X*\M&4/`4[M8`5M@[".!SX]9;*[CPT]-<B"A%]L[X?O`C$/A[F\Y3(X
+M#X.]M7H-!=?G%`9OC;.)!SX\9K*[CP(R"LB`F7=FD8#I#3,.A[F_Y#(X#X"X
+MKJ&YMX;G:P0,"C<R/#X^.3\\,S$U"0$8:4[AEIJ&@H^)BXJ*M8J+B(Z,@H&'
+MFI^1ZN'SW4)^86]J%!80$!,3$Q,0$187%&II;&-G>GQV2$!?U=_$P\[*]/?W
+M]_?W]/3UR,[-P,?:V=S3T=155U%04U)=7%Q?7U]?7%Q=4E-345%65U=45575
+MU-37UM;6UM;6UM;6UM;6UM;6UM?7U]355U34UM;7UM?7U-35U=75U=75U=75
+MU=75U=75U=74U=34U-34U-34U-74U-34U-34U-34U=34U-35U-34U-34U-35
+MU-34U-34U-34U-34U5565-76UM?7U]?4U-75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U-34U=35U=75U=75U=75U-75U-75U=75U=75U-34U=75
+MU=755594U=;6U]?4U-35U=75U=75U=75U=75U=75U=75U=74U=75U=75U=75
+MU=74U-75U=75U575U=55U=75U=75U5555=75U=75U=75U=75U=545E?5UM?7
+MU]34U=75U=755=75U5755=5555555575U=75U=75U=75U=75U=34U-35U-75
+MU=75U=75U=75U=755575U=75U=75U=75U=55U5165]76U]?7U-35U=75U=75
+MU=75U575555555555=75U=75U=755=75U=75U=75U=75U555U=75U=75U=75
+MU=75U575U=75U=755=755=555%%75=;7U]?4U-75U=75U=75U=75U5755=55
+MU=75U=75U=75U575U=75U=74U=35U=55U575U=75U=75U=75U=755=75U=75
+MU=75U=555=545E?5UM?7U]34U=35U=75U=55U555555555555=3ED.GF^<71
+M271^>65E9V1D97A_<G9*3$1>5]?2WMK&P\W.R<C(R\C)SL_"P\;'VMC>W-/6
+MU]555E914%-34U)24E)24U)34U!045%65E=75%555=75U-74U-37UM?4U535
+MU=35U-34U575U=74U=75U5555555555555555%145%155%15U=155=75U-74
+MU=75U=755=75U=74U-55U55555545555U=75U=37U]545E155=75U=75U-?7
+MU-75U=7555545555U=75U=75U=75U=555=75U575U-34U-34U]35U=555555
+M5=75U-34U-74U=7555555=55U=37U5575%755=75U=545=75U=75U=74U=75
+MU=75U575U=75U=75U554U=745=75U=34U-34U-34U-75U=75U555U5555555
+MU575U=75U=75U]?55%=55=75U-34U-36U]755555U5755=75U=75U=755=75
+M55755535U-35U=55U=75U-35U=755555U=75U=75U=7555555555U=75U=37
+MU]155%155=75U=755=34U-?4U-35U5555555U=74U-34U=755=555%75U575
+M5=75U=75U=755555U575U=75U=75U=75U=55U5555555U=36U%54U=555=55
+M5555U-34U=34U=755555U=75U=75U=75U=75U515U=745=74U=75U=755=75
+MU=74U=75U5555555U=75U=35U-555555U=555%?5U=34U-37U%35U-74U-34
+MU-34U-755=75U=34U=75U5555U945=55U=74U]34U-34U=75U=74U-34U=34
+MU5555%555575U-34U]37U]545E155=34U-35U-?4U=55U=75U=74U=555%15
+M5=75U-34U-355=75U515U=74U-?7U]?7U-75U555U=55U=34U-5555555515
+M5=74U=37U%145%15U=75U-15U=35U=55U=34U=34U]?4U-75U5545%55U555
+MU-3455555=55U=75U=34U-34U=75U=75U=75U=75U=7555555=75U]?55515
+M55555=75U=77U]34U=7555155-75U=34U-34U=5555555%35U=35U=34U]34
+MU=55U=74U=75U=75555555755=75U=75U5555%75U-545%55U=55U=555%15
+MU=74U]?7U]34U55555555=75U=75U=555=75U5755=74U=34U=34U=75U555
+MU5555=5555555575U=555575U=75551755155=74U-75U-?5U=555555U=75
+MU-75U-75U=75U=75U555U-745575U=75U=74U=75U=755=755=755=755=75
+MU555U555U555U-?4U5155=75U=75U555U=75U-34U-75U57555555575U=55
+M55555%=45=555=75U=74U-35U=755=755=75U55555155%555575U=755574
+MU]155U555=75U=75U=?4U555U55555555=7555555%555574U-755=75U555
+M5=75U=55U=75U=555=75U=75U=74U-55U555U=75U=75U=37U%545%155=55
+MU=555-75U=75U=74U=75U=34U=35U=555575U554U=755=74U-75U-34U-74
+MU-34U=75U555U=75U=755=75U=75U=75U]?555145555U=75U=74UM34U-75
+MU=555=755555U=75U=75U5555575U=15U=35U-34U-34U-74U-75U=75U=75
+MU=75U=5555555=555=74U]155%55U=5555755%35U-37U]34U-75U=34U=75
+MU=34U-7555555%55U575U=74U-34U-75U=35U=75U=35U=55555555555=75
+MU=75U-?7U%545=75U-34U]35U-34U=7555755=75U=75U=75U=555=75U=55
+MU-345575U-34U-34U-75U-75U-75U=75U=75U=34U=75U=74U=75U]?45514
+M5=75U=75U-55U-74U-35U-55U=35U575U=75U55555555515U=55U=75U-34
+MU-34U=34U=74U=75U5555575U5555=75U=75U=74U-545U55U555U=34U=37
+MU-74U=55U=75U=75U=75U=34U-34U=755=74U-75U=74U-75U=75U-35U=75
+MU=555=75U=7555555575U=75U=34U5175575U=75U=155%75U-34U=75U=34
+MU-75U=75U=75U575U554U=345=75U-?4U-74U=75U-75U-75U=75U555U=75
+MU=34U=35U=75U-355%=55575U=75U=75U]35U-75U=75U5755=555555U=75
+MU=755574U-155=75U-35U=74U=75U=75U=75U=74U=75U=75U-75U=75U=37
+MU]155%555=75U=74U555U=74U-34U-34U-34U-74U=74U=75U=555-75U555
+MU=74U-34U-75U=75U-75U=55U=75U575U=755=75U=75U=34U554U575U=74
+MU-35U-?4U=5555755555U=75U=75U=75U-35U555U-355575U=34U-34U=75
+MU=75U=75U=75U=75U=75U=75U-75U=75U-355%=45575U=74U%55U=34U]35
+MU-34U-34U=755=755=755=555%1555545=74U-34U-?4U-35U-75U=755=55
+M5-555=75U575U=74U=77U]545%55U574U-77U=76U=755=15U%75U535U574
+M5%745%75U==4U=17UU55U-75U-?5U-75UU75UU75U-755=74U=355=555=35
+M5=755=3455175515U575U-34U=77U=34U=75U-75UU75U575U%755=755514
+MU=755555U=34U-35U=35U-35U=75U=74U=75U575U=75U5555=75U-755515
+M5=75U=74U-34U]?5U=75U=55U575U=75U-34U=75U=755574U-35U=75U-34
+MU-75U%75U=55U555U555U575U=75U5755=755=74U-545U555=75U=75U515
+MU-74U=75U-75U=755=75U=75U575U5555575U53555755=74U-34U=34U=35
+MU575U=75U=55U5555=555555U=34U5545%75U=75U-35U-?7U=75U575U=74
+MU575U=35U=75U=755=75U-35=V!^<W5$7]?2Q`4C)S(T&F>5A8V+MKNGOH^!
+M@[>SM)Z5AKRDO/8R)#D-_U8$-0D4XFDP)B,Y;(NUE`8,$N'E!#8Q;K&DI8T%
+M-0'AAI!B%)RSI;+T,#DTYHJ*DF??AK6%`#X["(:\O809`A/D]@<Q-F^WNKR4
+M-3T*99+Z$AGIMKZT$#\[-.NTM9U@1X2)G0(^/@&)N+R%!0P<^?,%-S74L*6R
+M\38\-768Z143E+>]C@0Y.@J?MK>=;GJ?@ND//SP?M+N\G0`+&O7S&S4/XK.Z
+ML60S/PK#AY-F%):TLX$".#L/AK.VDA=NEH?]"3PS;[&ZO>@.-`3`^1P.`)"R
+MN[<3/#X(XX.;=6Z6M;&9"#LY`8^]L9$2$>.95`@]-M*SI;/.-38&S>$1`@68
+MO;B(!SX^#Y&.AM5BEXNTE34[/QZUO['J&QG(D'<),S7IO:6Q9#8Q`?/H;@82
+MA+V^@@PX/@.:M8/#8NN.B_$W.SUOMKFQY`8'295Y#C$/G[^EM!`P,`'AD7\:
+M%8:RO84*.S\$@+>,]6_@@HUT-C@PT;.XMM0-`V;M>@\W`8>^NX@$/3,&E9[=
+M'6&!LK.6-SH\'8ZQCLP4\8:&;#$^-^B]N[1G"`QHX7X"-1N"N;B"#3\R!)*$
+MY1=R@+"V\C$Z/6RULXG1$UB:FQ8Q/PJ9OKN*%C0(%.%U`0D6CKF^A`LY,AF%
+M@.IN2(&QM7XS.C#5MKV)2AEDDY,2-CT-@;B[CAHV-1;M500-88NYO)`W.#,1
+M@8^08$R'MXX7/3LV[K._CF$$%>N5'S8S!X^[N(,`,#06ZO`>!D2UOK/^,#LP
+M8(^+GV1TA;6`&3TX-9Z\OXP4`!+DXAXW-AV*NKF%#C(T%9'B%AKUM;^V>#(Z
+M,=6*M)MG>)Z.A0<\.0Z!OKZ#'P\:R><?-31BM[J_D30\-VR>EVT=Y+6]M1$\
+M.C?BM[::8V*1@Y,`/#\!C[B^AP<+!]7E$@L(4+:ZLOXQ/C1^A)]P%N*ULH\%
+M/CLUG;"QFFL4[X?N`CTR'K6ZOYX"-`!%YQ8/#>>PN[!^,CDTTH"$T&OLB[&!
+M`SDX#H>RL)L1'?*9^`TR,&ZVI;V4"#8"1>UI`P:4L[BW$3PY"N&/@/!OXHZW
+MF0\X.0&,O[.<'AI%D,<-,S=0LZ6R\S0P#52590<8G;.YBP0^.0F1BHWD;>>-
+MBI0+.#\9M;FSE@0&9^I3#3$*XKVEL7XQ,PW(DUP8$YBSOX(".#D-A;>)X6GD
+MI:*DLHF23QP`"34P,C\^.3\_,C`W"@T$$&3(Z)R$@(V.B(JUM;6*BXF/C8.&
+MA)F2E^_ESE)V9&)H%1<1$!,3$Q,3$!$6%Q5K;F)A9'ES=$)84=;>Q,+.RO3T
+M]_?W]_3URLG/PL'$V][=T];45U)24U%04E)=75Q<7%Q<75U34U!15E=75%55
+MU=74U]?6UM;6T='1UM;6UM;6UM;6U]?7U-34U=75U=74U-75U=55U555U=75
+MU=74U=34U-155U34T=;6UM?7U-?4U-74U=74U-34U=35U=75U=74U=34U-34
+MU-35U=75U-74U-34U-34U=74U-34U-34U-34U-75U-75U=34U-35U-74U556
+M5]76UM?6U]?4U-75U=755=75U=75U=755575U=75U=35U-74U=75U=75U-34
+MU-35U=75U=75U=75U=75U=5555555575U=75U=75U=755597U=;6U]?4U-35
+MU=75U575U=75U=755555U=75U=75U=75U=75U=75U=75U=75U=75U575U=75
+MU=75U=75U=555575U575U=75U=75U55445?5UM;6UM?7U-35U-75U=75U=75
+MU=5555555555U=75U57555555575U=75U=75U=55U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U5115U76UM?7U-35U575U555U=55U555555555555=75
+MU=75U=75U=75U=75U=35U=75U575U=55U=75U=75U=555575U=75U=75U=75
+MU=755%97U='6U]?4U]34U=75U=75U=555555555555555=75U=75U<B7E^'F
+MS]M!=7UY961D9&5E>'ES<4I)1D535M'=V\3#S<G)R,O+R,C.S\W#QL?$VM_<
+MTM#1U]155U%14U)27%U<7%U=4E)34U!045%65U145=75U-?7U-55U=74U-34
+MU-34U%75U-34U-75U=75U57555755555555555145=555U555=75U=75U=75
+MU=75U=74U=75U=755=755=55U5555=75U-355%145575U575U=75U=34U=55
+M55555=55U=75U5755575U=755535U=155=75U=34U=75U-75U=75U=555=55
+MU=75U=75U=75U=75U=74U-545U14U=75U=75U5545=75U=75U=75U=75U-35
+MU=75U=75U=555%555515U=74U=34U-35U=75U=75U-74U=755=7555755=55
+M5575U=34U5145575U=75U-35U=74U]7555555=7555755=755=755=75U554
+MU=355%75U=34U]?7U-3555145%145%75U=74U-34U-35U=75U=55U-355%14
+M5=75U=75U=755%75U=34U=74U=75U=75U=755555U5755575U=55U=75U=34
+MU-34U-74U-35U=75U555555555555=555=75U=74U-555%15U=75U-34U-34
+MU]?4U=755575U=55U575U=555555U5545-74U555U=74U=75U-75U=75U555
+M55555=75U=75U-75U=75U=75U=34U5145%755=75U=74U515U=74U-34U-74
+MU-74U=34U=34U=75U554U=755-75U=35U-34U=75U=75U=35U=75U=755555
+MU=75U=75U=75U-3555145=75U=75U=75U=?7U=75U555U555U555U5555=55
+MU=755575U-15U=74U-34U=35U=74U=75U=75U=75U=75U-74U-35U=75U=75
+MU-555%55U=75U-75U=155=34U-34U-34U-34U55555555555U=555-75U515
+MU=74U=34U-34U-75U=75U=55U5555555U=75U=75U=75U=34U5175%75U=35
+MU-34U=74U]75U=75U=75U575U=75U=74U=75U-55U=345%75U=35U-75U-75
+MU=75U=55U55555555575U=755=75U=75U-?455155=55U=75U=355535U=74
+MU-34U-74U=75U=75555555555%155=545555U=75U=75U-75U=75U=75U=35
+M5=75U=75U=75U=75U=74U-155%55U=75U=74U]35U-?4U555555555555575
+M5=555555U5555=75U555U=75U=75U=74U=75U575U=755=74U=75U=75U=75
+MU=55U=34U5175555U=75U=75U%55U=75U=74U-35U=75U=75555555555514
+M55555U55U=75U-74U=75U=75U=75U=755555U=555%5555555555U-355%=4
+M5=75U=75U=75U=37U=755=7555555=75U=75U-35U=755575U=545=75U-74
+MU=35U=75555555555555U=75U=75U=75U=75U=74U-555%555575U=75U=55
+M5=75U=35U=75U=35U=34U575557555555%75U514U=74U-35U-75U=34U-34
+MU-35U=755=55U=75U=555555U=37U5545555U=75U=75U=74U-35U=755=75
+MU=75U5555=75U=75U=55U=345=75U=75U=75U=75U-75U=75U=75U=75U=75
+MU=74U-34U=35U-?555155=75U-34U-34U575U-37U-34U-35U=755=75U575
+MU=755574U-145=75U-34U-35U=74U=75U=75U-755575U575U=755=75U=74
+MU]145%55U=75U575U575U=?4U575U555U=75U=75U=55U=75U=555-74U%55
+MU=74U-34U-74U-34U-34U=75U=74U=35U-75U=755=75U=?7U%545575U-74
+MU-35U-54U=74U=34U-34U-34U-35U-75U=75U=55U-355=74U-?4U-34U=35
+MU-74U-75U-75555555555575U=555=75U-355%155-75U=34U=35U=77U%75
+M5555U=75U=75U=75U=75U=35U574U-15U=74U-34U-34U=75U=75U=555=55
+MU575U=75U575U555U=74U-545%155=74U-34U-345=74U-?4U-35U=75U555
+MU555U=55U=545=75U515U=75U=34U-34U-75U-75U=75U=75U=75U5555555
+M5575U=34U5545574U-74U-34U-74U]?5U-555=7555755575U5555575U555
+MU-345=75U-34U-35U=75U=75U=75U=75U575U575U=75U=75U=75U-355515
+M5555U=75U=74U555U=74U-75U-34U-75U=75U=74U=755574U=555=74U-34
+MU-35U=75U=75U=75U=555=75U=755=75U=55U=74U]555555U-34U-75U-35
+MU-?4U=75557555755=75U=75U=55U=555=75U575U=74U-34U-35U-75U=75
+MU=75U=75U=75U=75U=55U=55U=34U5545%55U=75U=75U5545575U-34U-34
+MU=75U=75U=75U=35U=55U-755=75U=34U-34U=35U=75U=75U=75U=555555
+M5=75U=75U=55U-3555155=75U=555=755=74U-755=75U575U=75U=55U575
+MU=75U574U=555=74U-34U-34U-74U=7555555=555=555=55U555U555U=74
+MU]155%75U575U-75U=755575U=34U-34U-34U=75U=75U=75U=755=75U515
+M5555U=35U-34U-34U-35U=75U=74U5755555U=75U=75U=?4U5545=75U-34
+MU-34U-74U]?5U=75U=75U-75U=75U=75U=74U555U-345=75U=34U-35U=75
+MU=75U=755575U=75U=75U=75U575U=74U-3455155555U=555=75U554U=74
+MU-34U-34U-75U=34U-35U=75U575U-55U=34U]34U-34U-75U-74U=75U=75
+MU5555=55U=75U=75U575U-545U15U=75U=34U-34U-37U=75U=75U=75U=35
+MU=34U=35U=35U=34U%55U=74U-35U-75U=55U=75U=75U=74U=755=755=55
+M5=75U=74U5175555U=75U-34U-155=75U-34U-34U-34U-74U=5555755555
+MU=7555755=75U=75U=34U=35U=74U=75U=555555U=55U=75U=75U-355%=5
+M5=75U=75U=74U=37U]35U=75U=75U=75U=74U=35U=75U574U-15U=75U-74
+MU-34U-75U=755=755=75U=75U=75U=55U=55U=75U-545%555=75U=74U=74
+M5574U=74U-34U-34U-35U-75U=7555555-755515U=37U]?4U-34U=75U=75
+MU=75U575U555555555555575U=37U5145575U=34U-34U-75U-?5U=75U=75
+M5555U575U=75U57555145=755575U=35U-34U-35U-34U=34U=75U-34U-75
+MU-35U=75U=74U-355%=45%75U=75U574U%55U=74U-34U-35U-74U=35U=55
+M55555535U=545=75U-34U-34U-75U=75U=75U=75U=75U=75U575U575U574
+MU-545%55U=75U=35U-75U=74U555555555555=75U=75U=75U=555=75U%75
+MU-74U-35U-75U-34U-75U555555555555555U=75U=75U=37U5145%55U=75
+MU-35U-355=75U-34U-75U=75U=75U55555555514U=555%75U=34U-34U=75
+MU=555=55U575U=75U=755=75U=75U=55U-3555155555U=75U=34U-37U]35
+MU=555=55U=75U=55U=75U%75U575U=555=75U=75U=55U575U=74U-754WEA
+M<G-,1%#6W]8W(B0R"1/1F(:+B[:POK.*B[6YHZ:W]7^2FQ<S/@J8L8,!/SXT
+M#38Z)36$L8T&/#(!=A8)"?*RI;-E-PI\A)UN%(>YI+8=,S=AAIAE%)BROH`(
+M.#(4AH1W%I&VL)0Q)3)RCH_E:96UM-\S)3'HMK>6;.:/CV(R.S6$O;"0$7:%
+MA!(R.0V.N;.5!1/HD1DS/1NTN[/R#0%>XAXV,6&PI;%E-0EMYQ$U"_B]I;0<
+M,#07[&0"`)"\NXD#/#$4D/T;&9B]OH8*.3!CA))N%(6RLI0Q.C'<CX#68YBV
+MME\R.C:5M(CG8Y&*B1<\.@J$L[?O%/F#AAL\.0*.O['A&6>>D@<]/!^WN+';
+M`![A[`<S,'ZSNK9C"P-`Y1HW->.\NK4>-@AAX1$(`I^_NX\",C1BE'D!&X>_
+MOH0U/C=XF.8=%(&\O>@Q.#?-@YYA88:SMG8].C27BH/6986WBQ,_.PF'L8CS
+M;):)@0<^.0&.O+3V$_6&G0,_/!*WN;=:!V^0[`(],$ZSN[1N#QK^_``P->N_
+MNHL8-`)!_04T#9JYNX(",PA][18/&H"YN9LU/#5/DW4$%(V^O>,P.37QA^X6
+M9(V\L6<].`N6CIIZ<8"PBAX^.`^'MX)<>X6T@`$Y.0>.LHE4:I2,G`PY/!:W
+MOXIQ&=":X@X_,%2RN(MJ`Q25RP\R-):_NXX;"@7VV@,V#(>XN(`",0)1_`4+
+M!8V[OI\U,@[7ZA0#%(FXO?LP/PCYF%89?XB^L6T\.0Z6@Y1J7XZRBAH^.0*$
+MM85Y6X.V@`(X/@6/L8-W99J+G`@X/&JWO8]Z$>B!X0HY,,ZROH\5!$^=W0H\
+M-)._N((;#Q'M0@DP#(&XN8<"-`3W5P,T!8Z[OI,U,`/(^QL/%;6[O?4Q/0WD
+MD6D'=;2YL6H]/P*6A,\3PK6\B@0Y/@&$CI%L](FP@`P[/AF,MX5E4(.TG34[
+M/&VTLX%F8)F,Y#0X,/RSO(,4'>"%03<^-)^_OH$8`7F7934R#(.XOIH#"!/G
+M>`DV!8NZOY0*-@3\5@`+:+2ZLM8Q,P'FX1X`7[:[L18]/0&7G6$9^+:^B@8^
+M/P2:@/\4X+2R@`D[/!*-BY!C^XFVDC0Z/62TMIIC4H"(_#8[,.:PLH05;IV&
+M?S$Y-)B_O(4>&/^=;S8\#(*XOYP``F'N:#0P&K6ZO.P(-1/D9@DU;[:ELTPV
+M-AOFU`$-VK"ZMA`R,P64[A(%[;"YBP$^/1F8FWT0E+&\@0L[/1:#C>=NE;2P
+MD#<Z,G"*M9%BYXZUR3`Z,>VQL9]J0X:"83,X-9J\LI\=%Y:8%C`_#8RXO98&
+M!,:4$S8S&+2ZLN0.#V+A%S4W8[&EL'PW-!#F9@\.\+.ZMQTS,1SKQP<&E+*X
+MB`,_,Q*<EQ<=DK._A@HX,VN!AT)JG;&SEC8Z,$2(CN9BEK2TT#,Z-NBVM)1N
+MY(R,:#([-86]L9<0<X6%'#(^`H^YL^@%$^N1&#,R'K2[L_0-`5#B&38V9+"E
+ML68U"6+G$34+Y+VEM!\P-!3L9P(`D[R[C@,\-A20\!L>F[V^AS4Y,6.$DVD5
+MA;*RE3$[,=V/@5%CF+&W1S(Z-Y6TB>5BD;6)%CP["X2SM.(4^8.'&SPY`(Z_
+MMN<99YZ3!ST]'+>DKJ&XMH'@:00,"C<R/#X^.3\\,S$T"``::G?DE)B'@HR)
+MBXJUM8J*B(Z,@H"'FI^0E>/\V$9\9FQJ%!80$Q,3$Q,0$!$7%!5I;&-F97]Q
+M24=<5=+;P<S(]?3W]_;W]_3UR\[-P\;%V-_=T-16459745!375U<7%Q<7%Q<
+M75U24U!15E=45575U=34U]?6UM;1T='1T='6UM;6UM?7U]?7U]34U-75U=75
+MU=75U=75U=75U=75U=75U-345594U=;6U];7U]?7U]?4U-35U=75U=75U-35
+MU=75U=75U=75U-34U-34U-35U=74U-34U-34U-34U=75U=34U=34U=34U=75
+MU=75U=74U=555E?5UM;7U]34U-?4U-35U=75U=75U=75U=75U=75U=75U=74
+MU-34U-75U=75U=75U=35U=75U=75U=75U=75U=75U=55U575U575U=75U511
+M5E77U]37U-34U-75U=75U55555555=75U57555555=75U=74U=75U=75U=75
+MU=75U=75U=75U575U=75U=75U=75U5555=75U=75U=755%%75=;6U]?4U]34
+MU-34U=55U5555=75U575555555555555U=755555555555755=75U=75U=55
+MU=75U=75U=75U=75U=55U=75U=75U=544595U]?4U]37U-35U=7555555555
+MU57555755555U555U=75U=75U=75U=75U=75U=75U=75555555555=75U=55
+MU5555=555=75U=75U5115U76U]?7U-?4U-35U=75U5555575555555555555
+M5555U=75U^20Z>'YQ==)='YY9&5G965Z>7QR=DM"1UE1U=#>VL;#S,[)R,C(
+MR,G.S,+#P<?8V-[<W=#6U-545E%04U)24EU=75U24E)34%!145965U155=74
+MU-155-74U-?7U]?7U-34U-545=75U=75U5555=555555U5545%175U5555=4
+M5575U=34U-75U=75U5555=555=5555555%555555U575U=37U5545%75U=34
+MU=34U=755=74U%55555555555555U555U575U555U-355=75U=34U-75U=74
+MU=75U=75U=55U=75U=75U=555=555=75U-?55%=45575U=74U-34U-355%75
+MU=75U=75U=34U=75U=555=555575U=545=75U-34U=75U-75U=35U=75U=75
+MU=5555555575U=75U-34U]155U145=55U=75U=75U-74U]35U=755=755555
+MU555U555U=555=75U575U=74U-34U-34U=35U=555=555=75U-75U=75U=75
+MU574U-?7U%145555U=75U=755=75U555U=74U=34U-35U=35U=75U=755514
+MU5555U15U=34U-34U-34U-75U575U575U=75U=75U=555=75U=75U]?55%14
+M5575U=34U-74U-34U-?7U-75U575555555555=75U=755575U5545=75U-74
+MU=75U=75U=75U=755=75U=75U=75U=75U=75U=74U-545U145=75U=75U=74
+MU-55U=34U-34U-34U-34U-74U-35U=555=75U5145=75U=34U=74U-34U-34
+MU-35U-75U=75U=5555555555U=34U5545=75U-75U-75U-35U=77U-755555
+M5=755=55U=75U=75U=75U-355=75U-34U-74U=75U=74U=35U=75U=75U=75
+MU=74U-34U-74U-?555155=75U=35U-75U=755575U=34U-34U-34U-75U=75
+MU=555575U5545=74U-34U-35U-74U=55U=5555555=555=75U=75U=75U=74
+MU%555U155575U=75U=34U=75U-35U5555=75U575U=75U575U5555=35U535
+MU=34U=35U=74U=35U=755=5555555555U=75U=5555555=3455175%75U=75
+MU=75U=75U555U=75U-34U-34U=75U=555575U575U=755%75U=35U-75U-35
+MU-75U=5555555575555555555=755=75U]?55%155=75U=75U=75U=75U=34
+MU=7555555=755575U=75U=755=74U=545=75U-34U=75U=755=75U=755=55
+MU555U55555555575U=74U-545U155575U=75U-75U-555=75U-75U=75U=75
+M5=75555555145-7555145=75U=75U=74U=35U=35U5755555555555145555
+MU555U=35U5175%755=75U=75U=34U=77U]7555555%5555555=5555555555
+MU=755%75U=75U=75U=75U=75U=55555555555=75U=75U=75U=75U-355%=4
+M5%555=75U=35U=755575U-34U-34U-75555555555=755575U=545575U-34
+MU-35U-34U=74U=75U=75U=55U5555575U575U=37U]555U145=75U=75U=75
+MU=75U-?5U=75U=55U5555=75U=75U=555=75U535U-34U-34U-35U=755=75
+M5=755=755=55U=75U=75U=55U=34U5545%55U=75U=74U=34U-55U=34U-34
+MU-35U=75U555U=55U555U-555%555=75U=75U=74U=75U=75U=75U=755=75
+M5=75U=755=75U];455145=75U=34U-35U=75U=37U-75U-75U=74U-34U=34
+MU=75U=74U-55U=75U-75U=35U=75U=75U=75U=55U555U=755=75U=75U=77
+MU-555U75U=75U-34U=75U=755-75U=34U=34U-35U-35U=75U=55U=35U555
+MU=37U-34U-74U=75U=75U=75U=75U=55U555U5755=75U=37U-545575U=74
+MU-34U-34U=74U]155%55555555755=75U575U555U-755=75U=35U-74U=35
+MU=75U=74U=75U=55U=55U=75U=74U=34U]?455155=75U=74U=35U=755515
+MU=74U-34U-75U-75U=75U=755574U=545=74U-34U-34U-34U-35U=75U=75
+MU575U=75U=75U=75U-77U%545U15U=74U-34U-34U=35U-?4U=75U=75U=75
+MU=75U575U=55U=34U515U=74U=75U=74U=35U=75U=35U=75U=75U=34U-75
+MU=75U=37U5175%55U=75U=74U=74U-54U=75U-35U-34U=35U=35U=75U=55
+MU-755%75U=34U-34U-34U-34U=75U=7555555=75U57555555575U-3555=5
+M5=55U=75U=75U-34U-37U-75U=75U=75U=34U=75U=755=74U=155=34U-35
+MU=75U=75U=75U=75U=75U=75U=55U=75U=755=74U-545U55U=74U=34U=75
+MU=355=75U=34U-35U=755575U=75U5555=75U5145=75U=35U-34U=34U-74
+MU=35U-74U=75U=75U=555=55U=34U5145575U=34U-34U-74U-75U]35U=75
+MU=75U=75U=75U=75U=55U-355=74U-74U-75U=75U-75U=75U=75U=74U=75
+MU=74U=75U=34U-?455155=74U=75U=75U=75U535U-34U-74U-75U=75U=75
+MU=755575U5545%55U=74U-34U-34U-75U=75U=75U-75U575555555555=74
+MU]155%55U=75U=74U=75U-75U=?4U=75U=75U=55U=75U575U=75U575U=75
+MU-74U=75U=75U=75U=75U=75U=75U=75U=35U=75U=75U=?7U%545575U=74
+MU-34U-35U=545=75U-34U-35U-55U=75U=75U=75U=75U=755575U=75U=75
+MU=55U=75U=75U=55U=75U=555=755555U=55U=74U=34U=35U-75U=55U=74
+MU-75U=755575U=75U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75
+MU=75U-75U-34U-75U=74U575U=75U=7555555=55U=755%355=75U=74U-74
+MU=74U=74U=75U=75U=755=755=55U5555=55U=75U=75U57555555=75U=74
+MU-34U-35U=75U555U=75U5555575U575U]?4U=755=755=75U=75U=75U=75
+MU=75U=34U-35U=75U=75U=55U=75U575U=75U=74U=75U=75U=74U-75U=75
+MU=34U=35U-34U=75U554U=74U-74U=35U=75U=75U=75U=75U=755555U555
+MU=75U=75U=75U=75U575U=75U=75U555U555U574U575U=75U=75U=35U=35
+MU=75U=?7U=75U575U=555=55U=75U=75U=75U=75U-34U-75U=74U=75U=75
+MU=75U=75U=75U=74U-35U-35U=355=75U=74U-75U=75U=75U=155=75U-34
+MU-34U=75U=75U=75U=75U=55U5545575U=75U=75U575U=75U=74U=75U575
+M55755=75U=75U-75U=74U-34U=35U=35U=34U=74U]75U555U=55U=75U=75
+MU=75U=75U=75U=74U=5555555575U=75U=35U=75U=75U=34U=75U=75U=74
+MU575U=75U-75U=75U-75U=755575U=34U-74U-74U-75U-75U=75U=755=75
+MU=55U=75U=75U=75U555U=755575U=75U=75U=555=55U=3555555555U=75
+MU-74U-74U-75U-3555555575U=75U=74U=75U=75U=75U=34U-35U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=74U]75U-34U=74U-55U=74U=74U-55
+MU-74U-34U-34U-75U=555=755575U55555555575U=75U=75U=75U=755=75
+MU=35U=5555555555U=74U575U=74U=74U-74U-34U-75U=37U-5555555=75
+MU=75U=75U575U=75U=74U=35U-75U=75U=55U=74U=75U=75U=75U=75U=75
+MU-74U-35U=75U57555555%75U555U=555=75U=75U-35U-35U-74U=755=75
+M557555555555U=35U=75U=75U=75U=75U-34U-75U=7555755575U5555555
+M5=75U=75U=75U-75U=74U]355575U=75U5555=55U=75U=74U=75U=74U-75
+M5555U=55U=55U=75U555U=75U=75U=55=&!Y<W1$7M?3Q!HC)C(T!6;KA8VT
+MO;*UL+ZEMY^2B;NGM.F5B(<,)"0*EI$'-03C93TC(37AY04&E+2##SH\;X*%
+M2OZVNK`1,S7AC99H[;.DL!TP",V3%P)/L*6T!#`.048/-VRPNHL$-`3V:#<P
+M;[.[B!X-9903,S)ML[F/$1V7F!L\/&RPO($538"&!CD_:;>QG&R4M8(#.S]N
+MBHCE9X>PC@([/&V-A&1]B;R+`C@S9H3F'W6TN8H#/S9_D6H#3K&ZM0$R"%CR
+M!@MUL[J*!S8&]GH*-W"RNH@%"13L$3$P>;*XC!\$_)`:/3UEL[^!$&^;F@8^
+M/&>QLYAJYXZ!`C@_9[2TZ&&;MH(,.SQDB8-;<HRRC@PX,GB`D1=!M;Z(##DQ
+M<YYQ!%&QNXL"/#5'[1L,U+.ZB``P`L-`#S54O;J)!C4?YQ4W-ER]NXP%`TN5
+M&#(S1K*^@!\2D9P&/SU+L[V%%E6#A0(X/'>VMI9LD[6!#SL]=K6.]7Z`L((.
+M.S)WC)IB1(J\CPXX,4B$\![?MKF.#S\T6)<6`,FSNXX-,@_8SP`(]+VZCP`V
+M!/]B-33*O+N-!PYO[!PP,<*\N(`:!>&6!SPSV[*\A!)FA9X".3+1L+"3%>Z)
+MA`X[/52WM>=GA+:!"#LR4(B!<$.)LH(+.S!3@)41Q[>^C0@Y-U>?9P3RL+B,
+M#CP(V^8%#/J]NXP,,`'Q?`D*Y+R[@@,U$.80-C?EO[B`!P#5ZP0R,?F\OH09
+M%I*0`CXS\K*RG!'*@ID..#+WL;?N;9ZUA`L[,\BTC-1TC;"&-3LPP(R?:<2T
+MO(`U.#;$A-P9^;"Y@PL_-<V4$@#@O;N""3(-]]4"#NR\NX,,-QCZ%30U[[^[
+M@0`/9^(:,S;MO[F$!!OOE`,\,..\O9\<?823#CDSYK.QE164B9D*.S/EMHOQ
+M>8&VA34Z,/Z+AV73B[*'-#LV\(#M$/BVOX$U.37VG&\$[;*X@0H\#_#X!P+J
+MO+N!"3$'_F$+")2^NX8-"A3G'S$TE+ZYA0$!R.\!/3:5O[^>&A6<E`\^,>N]
+MLY80Y8*3"CLP[K"TY&.%M)XT.C#BM()-7HZPFC<[-N:/DVO^M[R$-S@T^H5`
+M&>^SOH<T/PG^ZQX!E+RXAPHS`/Y,#`^1OKB$#C<<Y1$W"I.YN(4"#''@!S,T
+MD+Z_G@<?E>X,/S:1O+*1'T2'E`HX,9>RMN-JDXB0-#LQE;:)W':#MIPW.C;I
+MBX5A]K6RF#8[-..`^A#LL+^:-SX+YY(4!9:]N84T/0+E]`$#DKZXF@HQ&N5I
+M"@Z<N;B8#PMOY!HQ-9^YOIP`!_[@`CTWG+Z]D05LGNX+.3:=O+#O$>V-E30[
+M-I"PM?1GA[21-CHVEK2`<]J(L)(Q.S3JC)<5XK:]G#8X"NR%?QZ6LKZ>-S\,
+MX>P;!IR_N9XT,P?D?P\-F;FYGPLT$>4<-PB;N+Z2#`);Y``R-9NYO)8&$I?A
+M"3\TF+^S[A_;ANPT.#>>LK?_:9^(ZS8[-YVVC$5$C+:4,3LTD8N98N2TLY$Q
+M.#65@?40E[.\DS$^#N^0$1J<O+Z2-ST!X-P#`9JYN9,U-A_D%S4,A+B^D0D)
+M9OH',`B$N+^4`@3F^@\\-82YLNX%9YCG-3DTA;RV^Q&5C>`V.S28LXO1>8&U
+M[#`[-)^TAGKSBK'H,#LUD(SI%96QO94P.0F4FV$?G+V_ES$]B*^FOK2$_A4$
+M#`LQ,S\_/C\\,C`W"@P''6#3[).%@8*/B8N*BHJ+B(F/C8.&A)B=E^[D]%1U
+M>F-N%1<6$!`3$Q,3$!$6%Q5K;FUA9'AR=$Q;4]?<Q<#/R_7T]_?W]_3URLC/
+MPL''VMG<T]'455=64%-275U<7%]?7U]<75U24U!15E=45%555=34U-?6UM;1
+MT='1UM;6UM;6UM;7U]?7U-35U-34U-74U-74U=75U=75U=74U=34U-34U-74
+MU-34U-34U-34U-34U-35U=34U-34U-35U=75U=75U-34U-34U-35U=75U=74
+MU-34U-74U=74U=74U-34U-34U=75U=75U=74U-34U-74U=75U=75U=34U-75
+MU=75U=75U=75U-75U=75U=75U=75U-35U-75U=75U=75U=74U=75U=75U=55
+MU=75U-75U=75U=75U=75U=75U=75U555U555U=75U=75U=5555755575U=75
+MU=75U=75U=75U=75U-74U=35U=75U=74U-74U=75U575U5755=75U=75U555
+M5555U=75U=75U=75U575U=75U=34U=34U=75U=75U=74U-75U=75U=75U=75
+MU=75U=75U5555=75U=75U=755=5555755=75U=75U=755=75U=75U=35U=75
+MU=75U=75U=75U=75U=55555555555=75U57555555=75U=75U=75U=75U=75
+MU=75U=75U=75555555755=75U=55U5555=75U=75U=75U=75U=75U=75U=75
+MU=75U5555555U=75U=75U=755=75U=75U=75U=75U=75U=75U=74U=75U=75
+MU=75U=75U-34U-35U=75U=75U=34U=75U=75U=7:ZY'@X?7;64AP>7MD9&=E
+M9'AX?7-T2T%%4U?1TMO$P,+/SLC(RLO(R<[,PL''Q=C>W]+0UM?555=14%!2
+M4E)=4E%27UU24U!04%%65E=75%555=35U=?6U]?4U-35U-35U=34U-75U=75
+MU555555555555%155%5555555555555555755=755=75U=75U=74UM175575
+MU=75U=75U575U575U=555%75U=75U=55U=75U=75U575U555555555555575
+MU=75U=75U=75U=75U=75U=75U=555575U5555=76U5155=75U=35U=75U=35
+MU=75U=75U-75U55555555%755=75U=755=75U=75U=74U=75U=755=755=75
+MU=74U=75U=75U=75U=55U=75U=?55U14U575U=75U=75U-75U=75U5155575
+MU=75U=75U=355=75U=75U575U=355=75U=75U=75U=75U=55U=75U=755=75
+MU575U575U=74UM57U5155=75U=74U-34U-75U-75U=?4U57555555=755=75
+M5=755=75U=55U=35U=74U-75U=75U=75U=75U=75U=75U5555=555=55U=36
+MU5=55%55U575U=74U-75U]34U=545=74U-34U-35U=34U=34U=5555555=75
+MU=555=755=555=75U=75U=75U=75U=75U=75U575U=75U-;55U145575U=75
+M5=35U-75U-35U=77U-75U=55U=75U=55U=75U=75U=75U=75U=34U-35U=75
+MU555U575U=75U=555=755=75U575U=75U-75U=34U=75U=755=75U=55U=75
+M5%75U=35U=75U=35U=75U=75U=555555U5555=75U=75U=755575U575U=75
+MU575U555U=75U=75U=36U5=55=75U=35U-35U=75U=74U-75U]?5U=55U=75
+MU=75U=74U-34U-34U=74U=75U55555555=75U=75U-75U=75U=74U=75U-35
+MU-34U-;45-55U=75U=35U=555555555555=4U=75U-74U-75U=55U575U=75
+MU=75U=55U5555555U575U=74U=75U=55U=75U=35U=35U=35U=74UM145535
+MU=75U-75U-74U=755555U=345555551555555575U=75U=74U=75U=35U-75
+MU=555575U=555555557555555555U=75U=355=77U5=55=75U=34U=75U=75
+MU=75U=545=75U-34U-35U=5555555555U5555555555555755=55U=755=75
+MU-35U=75U=75U=74U=75U=55U=?55U55U=75U=74U=75U=74U=35U=74U-75
+M55555=555=55U5555575U575U=74U=34U=75U=755=75U=75U=75U5555555
+M55555=75U=74UM5755755=75U=74U=75U=55U=755%75U=75U-75U=75U=75
+MU575U575U=755=555555U=75U=75U=75U=75U=34U=75U=755555U=555=77
+MU5=55=75U=75U=75U=75U=74U=75U-35U555555555555=75U555U5555555
+M5=75U=75U=75U=55U=75U=75U=75U=75U=75U=75U=55U=?55%545555U575
+MU=75U-74U=75U555U=74U-34U-35U=74U=75U=74U=75U=55U=55U=35U-75
+MU=755=75U575U=75U=75U=555=75U-75UM575535U=74U=75U=75U=35U-75
+MU=77U=75U=75U=75U=75U=74U=35U=75U=75U=75U-75U-75U=75U-34U-34
+MU-34U=75U=75U-34U=36U%355=75U=75U=75U=34U=75U=155=75U=34U-34
+MU-34U=75U=75U=555=75U=55U=75U=75U-34U-34U-34U-75U=55U=55U575
+MU=75U=?55U15U=74U=74U=75U-35U=75U=74U-35U=755=55U=55U=75U-75
+MU=75U-75U-74U=75U=75U=75U-75U=75U=75U=74U-75U-75U-74UM175575
+M5=75U=35U-34U-75U=34U575U-34U-34U-35U=55U=75U=75U-75U-74U-75
+MU=75U575U=75U=75U-75U=755575U=74U=755=7755=55-75U=75U=35U=75
+MU=75U=75U-15U=555575U555U=75U=75U575U=74U-34U-35U=75U=74U=74
+MU=34U=75U=75U=75U-35U=75U-;55U175=74U=74U-35U=74U-75U-545=75
+MU-35U-34U-75U=75U=75U575U=7555555555U=75U-75U-355=75U5555=75
+M5575U=75U=74UM5655155=75U-35U=35U=75U=75U=37U]35U=75U575U=74
+MU-75U-75U=75U=75U=75U=75U=75U=75U=75U-35U555U-75U=75U-75U=76
+MU5=55-75U=75U=75U=35U-75U=355=75U-?5U=74U=34U=74U=75U=34U=75
+MU-75U=35U=75U=55U=755=75U-35U=74U-75U-35U=75U=35U=75U=75U=75
+M5575U=755=75U575U-35U=75U=35U=75U=555=755555U=75U=74U=75U=75
+MU=75U-35U=34U-75U-34U=75U-75U=74U575U=75U=75U-15U-34U-75U-35
+M5535U-75U=34U555U-355575U5555=7555555=555=75U=755=75U=75U575
+MU-75U5555555U575U=75U5555=75U575U=34U=75U-75U=75U-34U-75U575
+MU=555=75U=34U=35U=755575U-34U=75U5555=75U=75U=75U=75U=75U=75
+MU=75U=34U=75U=755=75U=55U=75U555U=54555"9F5P=D=;U-#%:B<A/#$#
+M:^::@HNUMK:_IJ"]@Y&!B+6=8&F$L[R$-28F/6V:X@$V-P,;#CPE/7JVO(("
+M/C\`F8^'_OV!MK?C"S(.@;JDL74)#&6<EFT>Q8N\L4@R)3#ZL;*"%``=\_P8
+M-#1BM+NRVC,[-MZ)B9%B39N-E`T\,A6QI;WB-3,/VY[F%!2=M["9"SL^'K2Y
+ML.`##13F4P<(&H:_OH$(.S\:C;:/QQ98F9X7-S(-@+B[C0(\,!V9A.1H78&W
+MC1\\.PJ'OKF"!34->>U^!@;EM[ZT$C\Z-9RPL)L1'EZ0]``Q-?FRI;%I/3X(
+M[H^&R6SCC8[V-#DS5[*ZL7PT-@3GE6<;;X&SL.\V.CUDMK^U3P$%5NQN#C40
+MB;B_DC<[,VZ/M85[8I6`G00S/02+NKZ8"#(U89Z19!;CB[&!`#DX`X^YO9X&
+M"1OU_AT/!I*RN8D!.3D-A[&U[A!LE)E\"S,(G;^ZM1H],@&0@)!G99FUB6<P
+M.#:4O+N*$30+%^W+'@9UB+RQ2#(Z,>6QLH)N!&GIX!@T-&>WNK+V,#DWT(^,
+MZVS5A(^4#S\R%K:EO>,U,`W?D\,2%Y^VLY@+.SX9M;ZQX0$`;>-/``H%@;ZY
+M@`@X/!N#MX+6%,J$FQ$V/0V`N+N-`STQ$IR;P!30@K:/'#PX"H2^OH,:"@!T
+MXV(``.2VN;<0/SLUD[&VGA83]YS*`C`U_;*EL6\R/P[M@H57;>N.B/(T.3-:
+ML[JQ<C0T&N3L:P1N@+*SZ#8Z/6&WO(I)!Q_W[A4(-!*(N+^=-S@P:(V+F61D
+MDXV?!S(]!XBZOI@),PIGG>AI$.^ULX,`.3D"C;ZRGP<,'?_W&`D!DK*XB`8Y
+M.0V$MHOB%GN2FGDU,@N2O[JU&#(S!I&&ZVUXA;2+9#`X-NJ\N(L6-0YJXE,%
+M`'2*O[%$,CHQ_+:S@&\8>I?C!3<W8+:ZO?PQ/C17C8/@;,N!B)<./ST0M[JR
+MX@HQ`]J70AD1F;&RF@L[/QB*O[;F!@=^[W("-`2!OKF`"3@\&H"T@%QHYX:%
+M$#$\#(&XNXT`,C83DIU"$<6,L8X</#L*A;^_@!@)!UGF%0T#Y;&YMQ$_.S61
+MMK><%!3@F,H,,S3WLJ6Q8S,\#^.`GG5MEHB*^30X,TJSN[9W-349Y.00!FB"
+MO;*5-CH]8K2]B$X$$>;K%C4W'(B[OI\T.3!K@XZ29G.9CY@'/3P&B+N^FPXP
+M"&60Y!82Z+2R@@`Y.`*"O[.<!`(6Y\,%"@"2O;B+!SX^#9JWCN<409B$>#4]
+M-9"_NK4>,C,'EX7G:G^'MXI[,SLV[[VYBQ<+#&WC<08"<;6^L%8R.C'TM["&
+M;1Q%DNT$-C9LMJ6]Y3$^-%V#AOQO^8**D0X^/1*WNK+M"#<!P>MD!1.;L+V$
+M"SL_&XB\M^<$&ECK90PW!X&^N(,..3T:AHJ'36WH@X<0,#P.AKBXC0$S-Q&0
+MEF02S8ZSB!T\.S68O+R`'@\%V>00#@WZL;BV%SPX-9>WM),5;Y6%]`\R-\.R
+MI;!F,#T,YH:096V3M;3D-S@S?;"[MDD+"QWD]A\#%8*\LI8V.C)NM;*.3AMI
+MZ)43-#8>B+N^GC4^,6N`C9=@0X6(F@8]/P&)N[Z;##$.>9?.'!^5M[V-`3DX
+M#8.\LYT%`6GBW@8U`I*]NXH$/CX-F+6,_FK&A(9X-#PUEK^ZM1PS,`25F<X6
+M?8&QM7TS.S'CLKF(%0D#9.!D``Q\M;FSVS,[,<>TMH=B$/69[P8Q'ZBBI;./
+MDW4<``DT,#T^/SD\/S,P-`L`!!5E^NJ9A(.-B8B*BK6*BXB.C(*`AH6>D)7B
+M^<1;<V=M:!06$1`3$Q,3$!$1%Q05:6QC9F5_<$I!7U33V,;-R?7T]_?W]_?T
+M]<O)S,/!Q-C>TM#6U51645!24EU<7%]?7U]<75U24U!145975U15U=74U]?6
+MUM;6UM;6UM;6UM;6UM?7U]?4U-34U-34U-34U-35U=75U=75U=34U=75U-75
+MU=34U-34U-34U-34U=34U-34U-34U-34U=74U-75U-34U-34U-75U-75U=75
+MU-34U-74U=75U=34U-34U-74U=75U=75U=74U-34U-75U=75U=75U-34U-34
+MU-75U-34U-34U-34U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=35U-75U=75U=75U=75
+MU=75U=755=75U=75U=75U=555=75U=75U=35U=75U=75U=75U=75U=75U=55
+M5575U=75U=75U=75U=75U=75U=34U-75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=55U=75U=75U=75U=755=75U575U=75U=7555555=75U=75U=75
+MU=75U=75U=75U=75U=5555555555U=75U=75U=55U=75U=75U=75U=75U=75
+M5=75U=75U=75555555555=75U=75U=555=75U=75U-35U-75U=75U=75U=75
+MU=75U55555755=75U=75U=755555U=75U=75U=75U=55U=/BD.WA\\1727=Y
+M>&1E9V5D>WA\<G=U0T1<4=;2V=K!P\S.R,C(R,G)SLS"P,;$VMG?TM/1U]15
+M5%915E!275U<7%Q=75)24U!045%65E=75%75U=34U-?4U-37U]34U-34U-75
+MU575U=75555555555=7555555%55U55555555555555555555515U=75U=75
+MU=74U=75U=75U555555555555575U=75U=555=75U=75U575U=75U-75U-75
+MU=75U=5555555555U=755=55U5755575U555U=34U=55U555555555755=75
+MU=75U=74U=755575U55555555555U=75U=755=75U=75U=75U-74U-75U=75
+MU=75U-75U=75U=55U=75U=555-75U=75U=75U=75U=75U=75U=75U5555=75
+MU=75U=74U555U=55U=75U=75U=75U=75U5555=77U5=55=55U=75U=74U=74
+MU=74U-34U-37U-755=755=555-555=5555755555U=75U=34U-75U575U=75
+MU=75U=75U=75U=75U=75U=75U-;55%55U575U=74U=75U=75U=755=755%35
+M5=75U=75U=75U=75U=755=555=55U=75U=75U=75U=75U-74U-34U-74U-75
+MU%755=75U=74UM5455355=755=75U=75U=75U=75U=75U-35U=7555555555
+M5575U=74U-34U-34U-34U-35U=7555755555U=75U=75U=75U=75U=75U=36
+MU5155=75U=75U=755=75U=75U=75U515U=75U-34U-34U-34U=35U=75U575
+MU5755555U=75U-75U=75U=75U=74U=74U=75U=75U=35U-;55U55U555U=75
+MU=75U-34U-35U-35U=34U5555575U=75U=75U-75U=75U-74U=34U=35U=75
+MU=75U=75U=75U=75U575U=75U=75U=74U]5755155=75U=75U-74U-74U-75
+MU-555=55U=75U=74U=75U=75U575U575U=75U5755=75U=55U=75U=35U=75
+MU=75U=35U=75U=75U=36U%155=55U-75U=75U-35U-75U=74U=74U]755555
+M5555U575U=55U=75U=75U=75U=34U-75U=75U=55U=75U=75U=555=75U=75
+MU=75U-;55U54U=75U=75U=75U=75U-75U=755%755=75U=74U-74U=75U575
+MU=755=75U=555=555=555555U=75U=75U=75U=75U=75U=75U=74UM545535
+MU=75U=75U=74U=35U=75U575U-35U5555555U5555575U=75U=75U=75U=35
+MU-75U=75U=75U=75U=75U=75U=555=75U=75U=76U5945%55U575U=75U=75
+MU=75U55555145575U-34U-35U-75U=555555U55555555555555555555=75
+MU=75U575U555U57555555555U=?55U555=75U=74U=75U=75U=75U-35U=34
+MU=555514555555555=755575U=75U=75U=75U=55U=55U555U5755575U=75
+MU=75U=75U=74UM5755155=75U=75U=75U=75U575U=555-75U=35U=75U=55
+MU=75U=75U=75U=75U5755575U=75U=74U=35U=75U=75U=75U=75U=755=36
+MU5=55-75U=75U=75U=75U-75U=35U=74U]3555555=75U=75U=75U=75U=75
+MU-75U=34U=75U=75U=55U=55U=75U=75U=75U-34U-34U-'55%55U=74U=35
+MU=34U=34U-35U=745535U=74U-35U-75U=75U=75U=75U=75U5755555U=75
+MU=75U=35U=75U=35U-35U=75U575U575UU575535U=74U-74U=34U=34U=35
+MU=75U-?4U575U=75U=75U=75U=75U=75U=35U=75U=75U575U=75U=74U-74
+MU-75U=75U=75U-35U-36U5155=75U-75U-34U-35U-75U555U5575%55U=34
+MU=34U-35U-75U=75U575U=55U555U5755555U=75U=75U=75U=55U5755575
+M5=75U=35U=55U=55U=75U=75U=75U=75U=75U=74U575U555U555U=75U=34
+MU=75U=74U=74U=34U=75U=75U=55U5555=75U=75U=74U=75U=75U=55U575
+MU=75U=75U=75U=55U=75U=155%75U=34U=74U-34U=35U-75U=75U=755=75
+M5555U=75U=75U=34U=74U=75U=34U=75U=75U=75U5555=55U=75U=75U-74
+MU=34U=74U=75U]5555555=75U5755=75U=75U=75U=34U-34U-5555755=75
+MU=75U=755=755=555=75U=75U-35U=75U=75U-75U=75U=75U=75U=74U575
+MU=74U-75U=75U=75U=74U=75U=34U=35U=75U=74U=35U=35U=75U=75U=55
+MU=75U=75U=34U]35U=75U5555575U575U=34U=75U=55U=35557555755=75
+MU=75U=75U=75U=55U=75U=75U=55U=75U=75U=75U=55U=75U=75U-34U-34
+MU%75U=75U=755=74U=75U=74U=75U=555=75U-34U-34U=35U=75U=75U=75
+MU=55U=55U=55U555U555U=75555555555575U=75U=75U=755555U=75U=75
+MU=75U=35U=755=75U=74U-75U=75U=55U555U555U575U=755=75U=35U=75
+MU=55U5755=74U=75U=75U=75U=75U-74U-35U=755=755=7555755555U=75
+MU=555U15U=35U-34U-75U=75U=75U575U=75U575U555U=75U=75U=75U=34
+MU-35U-34U=755575557555555=55U=75U=75U=35U=34U=75U575U]34U=75
+MU555U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75U=74U=75U=75
+MU=5!9F1P<49:U=':8B0@/C$"%/J9@XJUMK&ZH:2"Z96"O;F+D9:(CG(\)SD6
+MB8(=,STU`S0X)32%L(@'/S\"^NP5$I:PN8D#/S'GL+*-Y):(M>$U/`V.NKZ:
+M!P-A[V((-6>QNK8</#T$EI!M'>^WLYTW)3+4M[:>:G^;AVDP/P*(N[SK#PD4
+MX&<,#O>SNK0$/S(<A816%9>WL>\Q)3#KL+"?$&F1GA`P/02TNK+&-340X'(#
+M`^^RN(X,.3(4@(#\;)>UM-4S.C>8O;.3&1/MD1TQ,Q:QI;!A-C<3[M4$!)"R
+MOH$U.S-[CH_C;>N(B6(R.PN#OK.5!QOWZ1PV-G"SI;82,S80E_X>'Y^RO)PV
+M.C#+M8ON;^",@Q`].0*(N+/E`@9$X!TT-?B]NK4'/3$7G.@7%INSL^`P.C:5
+MMK7I%?>!A!@]/P6TN[!>"0)YYQ,*#Y>\NXP,/S%MA))C:9JQMDP].C2;L[?L
+M$$":G`0R,A2QNK9L-0YAYA0/`9F_N88U.39W@)IV8)NVM14\.PF`O;?D&6"3
+ME`8S,4.RNK0<-@MC[&T#&X>_OY(V.#?PCH%49IFTCQX_.`")O[?-!Q;HXP8P
+M-."\NH@&,S5FE'$'$(&\LN0S.S25M8W!8)*(@0<_/ABTN;=V`Q[[^`8V")*^
+MNX(,/35\G<$>:8.]L7T].PN9MX[,;I2,F0,_/6NPN+1H#@3&\@0T`H2YN(0U
+M/S53A>`69(*RM!$_.`R`L(C8%^&!EPP\,U>RNXH<-0!0_1L+!(.YOI$V/C7_
+M@99O<X.PB04Y.0:.LHM$'=N;X`\]-^F_NX\'-P);Y!T,$H^YO?(S.0N5C)YD
+M=8&V@P,Y/A^TO(MZ!7^3]P\R"IFYN(`-,`Q1[Q0`:8F^L&<\.0^>BX1S=H>U
+MF@\Y/&VQOHEK`6CKTPPP#(&XN9L*,@_!EF8%?HN_MQ(^.0.&M(%Q>YN)E@LY
+M,\>RN8\=#!/F4PTW!XR[OY4W/0_EGEL<7(N]BP<Y.02,L8-_;9*#YC4^-I6_
+MN8,$"!C\40`U'(N[O<,P/`SJA/P7P8BS@@PX/Q.ULX-A%^B'WC4_-9JYN8<"
+M-`3QQ0<.:;6[L&(]/P.=@^ENRHZQA0L[/6>VO8-J'/V>=S4]#X.[OIP(-@;R
+M_!L#=+>XMQ\_/P:$CI!CR8RTD30[,_>SO(`3!$26934P!HFZO^PT,`;G[1,'
+MSK>^B@$Y/QB"M9]ATH"(Y3<X-I:\OX<:`&'I80LV'+6ZO5<Q,P;IEFX9Y[>\
+MC0X[/!>(MYMC3H2-6S8Y-86^OY@!#!7A9@XU;[:ZL&DR,@20GG(0[[>RA#4[
+M,G^TL9IH9YZ&83$_#X*XO)8/"Q#D>`T)1;&[MQD\/1N;A]H5Z+2QD38Z,/ZQ
+ML)L6%9>9%3$]!HNZO>0*-!SF30$"^K"XBP`^/1*&@^1OZ8JT_C$[-I&RLYX?
+M'>:0%C8S'[>ELT\W-Q_LP`4'ZK.^@@DX,FB,CNYMXHF(<3,X-82_LI`$!=KK
+M$#<V;;&EL14P,1R4YAP9D+.\A30[,TJ*BI5O^(*-:S(Y#XVYLNX#`7;C$#4U
+MU[.ZM!L],1.=E!40G+"REC$Z,>6WM)<5WX:'$C(_!HJ[L_<.#&'F%PL.XKV[
+MB`,_,!2%G&=JGK&Q]C,Z-Y"PMY40<YB>&#,]'+:ZL'TU"6[A:0\#D+RX@@@Y
+M,6&!A$%MG[>T93T["H2]MNX>;I&6!3,P8["EMA<V-6CL9``$F+R^A30X,5^/
+M@,%@DK6)$3PX#(V_L>4$$^WO!#$WP+VZM1LP-&F46@0<A+R]E#$[-^>*C/-B
+MEHB#&SP^!XJYMM`#&_#G!38*ZKRZC@,]-&.2\Q\4AKVPP#([-)"WB?]I[HV%
+M!CP\$K:[MV4&I:"ZL8*59Q@""#0R,CX_.3P\,S$U"`$::G#DE)B'@XR)B(J*
+MM8J+B(Z,@H"'FI^0E>/\VD1]9FQK%!81$!,3$Q,0$187%&IH;&-F97]Q2$9<
+M5-/8QLW)]?3W]_?W]_3UR\[-P\;%V-_=T-;55%904U)=7%]?7UQ?7%Q=75)3
+M4%!15E=45=75U-34U]?6U];6T='1T=;6UM?7U]?7U]?7U-34U-35U=74U=74
+MU=75U=75U=75U-35U-34U-34U-75U-34U-34U-34U-34U-34U-34U-34U-34
+MU=75U=75U-34U-34U-74U=75U-34U-34U-34U-34U=35U-34U-35U=75U=75
+MU=75U-75U=75U=75U=75U=75U=75U=75U=75U=74U-35U=75U=75U=75U=35
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U575U=75U=75U=75U575
+MU=75U=75U=75U=75U=75U=75U=75U=75U=555=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=555=55U=75U=75U=75U=75U=75U=34U-35
+MU-75U=75U-75U-75U=75U=55U=75U=75U=75U=75U=75U=75U=75U=755=55
+MU=75U=75U=75U=75U=75U=35U-75U=75U=75U=75U=755=55555555555=75
+MU575U5555=75U=74U=75U=75U=75U=75U=75U=75555555555=75U=55U555
+M5555U=75U=75U=755=75U=75U=74U=75U=55U=75U=75U=75U=755=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U-75U=75U=75U=75U=35U=75
+MU=7WEI3FY\#90TM\>65E9&=D97AY<G!U24=;4%30W-K&PLW.R<C(R<G)SL_-
+MPL#&Q=O>W]+0UM355%914%-275U=75U=4E)34U!145%145975%555=75U=34
+MU-74U];45-55U-74U-34U-35U5755=555%755515U-1555155%1455555=55
+MU=75U=555=555=755=75U5755575U575U=75U-75U-75U=75U=74U]575%35
+M5575U=75U=75U=55U=75U=55U5145=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U-75U=75U=75U575U5555=76U5=55%555=75U=755=75
+MU=34U=75U-75U=34U=555555U=55U=755=75U=75U=75U=74U=75U=75U=75
+MU=55U555U=75U5555=755575U=?55U145575U=35U=75U-35U-75U-35U=55
+M5U75U=75U=74U-35U=75U=5555555555U=55U=74U-35U=74U-34U=74U-35
+MU=75U=75U=75UM1755755=75U=75U=75U=75U=75U=75U=74U-75U5555575
+MU=755=7555555=75U575U575U-755575U=75U=755=75U=755=75U=75U=36
+MU%155=55U=75U-74U-35U=755=75U5755%15U=75U=75U-35U575U=555575
+MU=75U=75U575U=745=75U=755=75U-75U=75U=555555U==55U145=75U=74
+MU-74U-74U-34U-34U-35U-?5U=7555755575U=75U=75U555U555U575U=75
+MU=75U=755=5555555=75U=75U=75U575UM565575U=35U=75U=75U=75U=74
+MU=74U-55U=35U=74U-34U-75U-75U=75U=75555555755=75U=75U=75U=55
+MU=55U=75U=75U575U=76U5355575U=75U=75U=75U=74U=35U=75U=37U-75
+MU=55U575U=75U=75U=75U=75U=35U=35U-75U=75U=74U-34U=75U=75U=75
+MU=75U=;55U545575U=75U=75U-34U-75U555U=545-75U=35U=75U=75U=75
+M5=55U=75U=55U=555=75U-75U-75U-74U=75U=74U=74U=74U=74UM575515
+M5=75U=34U=75U=75U=75U=75U=74U]35U555555555555=55U575U=75U=55
+M5=75U=75U=55U=75U575U575U555U=75U=75U=34U574U=75U=75U=75U=75
+MU=75U=55U=34U555U=55U=75U=75U=75U555U55555555555555555555555
+M55755=75U=75U=5555555555U=955U545=55U=75U=75U=75U=75U=74U-75
+MU-?55554555455545575U=75U=75U=75U=74U=75U575U=75U555U=755=75
+MU=74U=34U=74UM5755355=555=55U=75U=75U=755555U=555=75U-74U-35
+MU=75U=55U5555555U=55U=55U=75U=75U=755=55U=75U555U555U5555=77
+M55=45-75U=75U=35U=75U=55U=55U=75U=37U%55555455555=55U=74U=75
+MU=75U=75U=74U=555=75U=755=755=75U=75U=75U=55U=?55U55U=75U-35
+MU=75U=75U=75U=75U=355=74U-34U-34U-75U-34U=34U-34U=75U=755575
+MU=75U=75U-74U-35U-35U=755=75U=75U]575575U=75U-75U=75U=75U=75
+MU=35U=75U]34U=75U=75U575U=75U=75U=75U=75U-34U=34U-35U=34U=35
+MU=75U575U=75U-75U-36U%155=74U-34U=34U-34U=74U=755=74U515U=74
+MU-35U-74U=35U-75U=75U=75U=74U=75U=74U-75U=75U=35U=35U=755=75
+M5=75U=?55U545575U-75U-35U=75U=75U575U=75U=?7U=75U575U=75U=75
+MU=55U=55U=75U=75U=35U=75U=35U=75U=75U=75U=75U=34U-34UM145575
+MU=75U=75U=75U=75U=75U=75U-15U=34U-34U-35U=5555555575U=75U=75
+M555555555=75U=35U=35U=75555555555575U=76U5=55555U=75U=75U=75
+MU575U=75U=75U=74U-7555555=555=75U=34U-34U-34U=74U=75U=35U=74
+MU-75U=75U=75U=75U=75U=75U-;45U55U=74U=34U-35U-75U=75U5555=75
+M5%75U=75U=75U=74U=75U=75U=34U-34U-75U=55U=555=75U575U=75U=74
+MU-75U=75U=74UM5755155=75U=75U=74U=35U=34U-34U-74U]?5U=75U=55
+M5=75U=755=75U=75U=74U-34U-75U=75U575U=75U=555=755=75U=75U=36
+MU5355=75U=35U-34U-34U-34U=35U=75U555U=74U-34U-35U=75U=75U=75
+MU=75U=75U5555=75U5755575U=75U=75U=7555755=75U=;55E1455755=75
+MU=75U=75U=75U=75U=35U=?4U=555575U=75U=75U=75U=75U575U=75U=75
+MU-755=75U=75U=34U=75U=75U=75U=74UM14U575U=55U=75U=75U=75U-74
+MU-35U=355575U=75U-34U-35U-74U-75U=75U=74U=74U=74U=75U=74U-34
+MU-34U-75U=755=55557755?55=75U-75U=75U=75U-34U-35U=35U=74U%55
+M5575U=75U=75U=75U575U=75U=34U=35U=35U=75U=355=555575U=75U=55
+MU=55U=35U=75U-75U=74U=74U=74U-34U-34U=755535U=74U=34U=75U=74
+MU=75U=34U=74U-75U=75U-35U=74U-35U=35U=75U=75U=75U=74UM545575
+MU=75U=75U=75U=75U=755=75U=74U]?4U=7555555555U=75U=75U-35U=35
+MU=74U-35U=75U=75U-75U575U=55U=75U=75U=31U5355-75U=34U-34U-75
+MU=555=75U=75U-545=55U=75U=75U=75U=75U=75U=75U=75U-75U-75U-75
+MU=75U-34U=75U=75U=75U=75U=?55U145=75U=34U=75U=75U=75U-75U=75
+MU=?7U=55555555555=55U=75U=75U=75U=75U=75555>969S<T)$5M;913TC
+M)3,/$\N%@(N+MK:SMK>PIZRGA1]FFI@<#G:VL_H\)S\#`S`R!8.-!B4G,6!A
+M``:>LK80/SUIC(#YX+>XMA@]-)&VB>SJM+V&-3@UA;:"0,2/M4D].@^,L(9I
+M<H:!&CDY$[:RA!!DA9P"/C/IO+V2&FF2X`@\"X*[L\H"$NY8"C,%M[JW$`H;
+M^V<*-T.]NHP"-@7^80D/G;FYDS0P&^)_`AJ-N+)D,C`0D%0$;;6XM`$^,7R$
+M^QW"M[^&-#LWZ8WJ:N^VL/HR.P^'BY!BE[2U$CDY'HJWD&V7BH$-.SW`L+:5
+M%.Z-E#0X-X2_MO<?_(1`,3X#M;BT;0=$D!0P,FZRNX\%#6;L'S$TE[ZYF0X(
+M;N48-P*"N[S=-S5LY!X+$[2ZMADR-7+I$P/0L+N,"#\*_9)I&Y:ROY<P.0R3
+MA7(7A;*Q:3\^&H"#TF&&LXD#.SU_BH_.>8&WF#0Z,9"QB-9EAXC#,SL.C+V)
+M96Z9@1$]/ARVOX(1$)>2!SPS^[R_FP<:Y^`",@J&N+WE#@;-Q@PQ!+6EL10T
+M`]]1#35^L[J(`S`"]]X`#9>_N)@T,@'L^00;AKZ]33(]&)SH$FF/OK<$.3)M
+M@9)K1XN]@#4[,>&)FF?WBK'L,SL+A[>$9/^)BQ4_.02*L(5N]8V&`3D]2;"S
+MG!-:AY8(.3:9O[/@!&&=VC0\#8B[MF<-%^AF-#`7L+J+&@L=YVHT-.*_NX<.
+M-QSD%0L#AKB_^38V$.]L#1*+N[$2/3%MDWP'2[>XC@XY-\&%V1WBL;R=,3@(
+MDX+G:I"QL7L_.0:#B.YCG+:)!S@_;+6U[F*<M9H*.S"5L[3X%9&-^38X"X*_
+MM'0=[85L,S\;MKF)$03.D1XS,-B\N88&`TK@!#`*A;B_Z`@.>_T'-P>+NK-O
+M-@M]_@0(8[&ZM0$R"-'B'@#BLKB$-#\/[9$6'IB\O<PR/P:9F&!H@;VW&#D\
+M%H*&='^"LX((.S/RM8)%3(*WE#$Z-86QC7%T@(YF/3@!BKV";V:%AQH_/&:S
+MO(0<%).6`C\VD[Z\EP$?[_,),@R/N[-U"03_0P@Q$[&EM!@W!O9S#@KRO+N#
+M#C`&^DX"`)NYONDQ,P65V@<=C+FP%SPS%IGF'WN*OHL-.3%%@)04]+2]F#8[
+M-)&)G6+CM+9>/3@"@[2>8.R*CALY/A:TL9UKX8R%#S@SX[*PZA++AN`T.36`
+MOK'3!'2<?38\!+>[M!<";>L4-C%TO;N-`0@4YA,W"YRYN9,*-!?D$PH'C+N]
+M>3`W:>\7#&BWN[0'/#1WD&T'\+"Y@#4^"N&%=1*1L[WF,CD-F(#,:)BPMQ,^
+M/A^,COMFA;:"##LRU[>*_F&:M9`W.S>8L[76:)^-3C,Y`HB_B&,3E)H3,CUJ
+ML+Z"'AKEE`<R-I6^OI\#`=+Z`#`/@KN]VPH-0<\#-Q^WI;8<,0]2RP$(U;*Z
+MC`\R#//D!0&3O+Z0,3P`E)42$H:_LV(\/!F%G&A@C+R*`#@R>HR$946.LX0T
+M.S:5M8%]WHZW]S([#H.Q@6?7@HP3/SX?M+*'%7>'F@`^,O>RO9T9;9_O"3\U
+MA[FR^0`1ZE<U,@:UNK9K"1GG934V9K.ZB08T!?EC"PB7OKB;-3$;X60,!X"X
+MO%8P,!*42084B[BV!3\Q8YCU&5&WOH(*.#?Y@.(7X[:RE3,X"9B)EVR7M[=K
+M/SD$C[218I:UC0`X/'RVMI5KE8^2-3@VG;VV^!+FAL0V/@^.N;1Z!=V<:#`]
+M$[&XB1\#?N@<,3;GO[B$`@YMY!@V#X:[O^0T-6SE&#49M;JP$3`U>N(=#'&Q
+MNX@,/`K%EA0$Z+*^GC8^#Y289Q"8O;-V/#X'A(%;;8:SM0<X/&F)C<5X@;:'
+M"CLP[;>.W7F&M>8P.PJ!LHEP8YJ":3T^!;2_C147DYD%/#):O:"LI[R*F=$3
+M`0XU,#(_/CX^/STP-@H,!QUAW.^2A8&-CXB+BK6UBHN)CXV#AH28G9;NY/54
+M=7IC:147$1`3$Q,3$Q`1%A<5:VYM861X<G1-6U#7W\7#S\OU]_?W]_?T]<K(
+MS\+!Q]K9W-+1U%5745!34EU=7%Q?7U]<7%U24U-045975%155=75U-?7U];6
+MUM;6UM;6UM;6UM?7U]?7U]34U=34U=74U-34U=75U=75U=74U=34U-34U-34
+MU-34U-34U-34U-34U-34U-34U-34U-34U-35U=75U=35U-74U-34U=75U=75
+MU-34U-34U-75U=74U-74U-74U=75U=75U=75U=75U=75U=75U=75U=34U-74
+MU=75U=75U=75U=74U=75U=75U=75U=74U-75U=75U=75U=74U-34U=75U=75
+MU=75U=75U=75U=55U=75U=75U=75U55555555=75U=75U=75U555U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75U=55
+M55555575U=75U=75U=75U=75U=74U-74U=35U=75U=75U-34U=75U=75U=75
+MU=74U=75U=5555755=55U=75U=755=555=55U=75U=75U=75U=75U=75U=75
+MU=75U555U575U=75U575555555555=75U=75U=755=75U=75U=35U=75U=75
+MU=75U=75U=75U55555555575U=75U57555555=75U=35U=75U=75U=75U=75
+MU-75U=755=75U=75U=75U=75U555U=75U=75U=75U=7'E9'AX<K:6DMS>7ID
+M9&=E9'AX?7-T2D%%4E;1W=O$P\S.SLO(R\C(R<[-PL'&Q=O9W]+0UM155U91
+M4%)=4EU=4E)24U)34%!04%%15E975U=55575U=35U-34U-?4U-34U-34U=35
+MU=75U=555555555455145%55555555755555U=75U575U5755574U5155554
+M55555=75U=555=75U=75U575U=555=5555555=75U=75U5755575U=75U-36
+MU%155=75U=75U-7555555555U=75U-75U=555%75U=75U=74U=75U=755=75
+M55555=755=55U=75U=55U=75U=75U-75U=75U555U555U=75U=75U=75U=75
+MU=75U=75U=75U=75U575U=74U]35U=755=755=755=75U=75U=75U=75U=75
+MU-75U-35U=75U=75U=75U=75U=75U=75U-75U=75U=74U=75U=75U=75U=75
+MU=755=755%=55=74U=34U=35U=55U=15U=35U=75U5555575U=75U=75U=75
+MU-75U=75U=34U=74U-7555555%555575U=74U-34U=74U=75U=75U=55U=?5
+MU=55555555755555U=55U555U=75U=55U=75U=75U=74U=755=75U=75U555
+M5575U=755=75U575U=75U=75U=34U-35U=75555555175555U=75U=35U-35
+MU=75U=75U575U575U575U575U=75U=75U=34U=34U=75U=755575U=555575
+MU5755575U=35U=34U-?4U-35U=755=74U-5555555=755555U575U=75U-74
+MU-74U=74U=75U=75U=75U=34U=75U=75U=75U=34U-75U=35U=75U=75U=34
+MU=75U=75U-75U=345=75U=34U-74U-74U=74U=74U=75U55555555=75U=55
+MU=75U=75U=75U=75U=75U=35U=555555U=75U=34U-34U=75U=55U=75U=74
+MU=74U]35U5755=75U-74U-35U-75U=75U=35U=74U=34U=75U=755=555=75
+MU=75U-74U-35U-55U575U=75U=74U=75U575U=75U=35U=74U555U=74U=34
+MU=75U=755=5555555555555555555=5555555=75U=75U555U55555755575
+MU575U=75U=75U=75U=75U=75U=75U575U-75U=34U=75U575U=75U=75U=34
+MU=75U=75U=75U=75U555555555555575U=75U=755=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=545-75U-34U=75U=75U=75U=75U=75U=75U575
+MU=75U575U=75U=55555555555=555555U554555555555555555555755=75
+MU=75U=75U=74U%5555555575U=75U=5555555555U=75U=74U=75U=75U=75
+M5=75U=75U=75U=75U575U575U=75U=75U=75U=75U=75U=75U=75U=755%75
+MU=75U-75U=75U=75U=75U5755555555555555=75U=75U=75U=55U=75U=75
+MU=555=755=75U575U575U=75U575U555U=75U=75U=75U-15U55555555555
+MU=74U=75U-75U=75U=75U=755=75U=75U=75U=35U=75U=75U=75U-55U555
+M5=75U=74U=75U=75U=75U=755575U515U=74U-34U-34U=75U=35U=34U-75
+MU=75U=75U=75U-34U-75U=75U-35U-74U-75U=74U575U=35U=75U=75U=75
+MU=75U=75U=75U=75U=?7U-75U=75U=75U=75U=75U=75U=75U=35U=75U=55
+MU575U-74U-34U-35U=75U=75U-35U=75U=35U-75U=75U=74U-34U-34U=75
+MU=545=75U=75U=75U=75U5555575U=75U-75U=75U575U=75U=75U=74U-34
+MU=74U=75U=75U]575%155=55U=35U=75U=35U=75U=75U=34U=74U-75U=55
+MU=75U575U555U=75U=75U=74U-34U=34U=75U=75U=75U=75U=755=75U=36
+MU%355=55U=34U-34U=75U=75U=35U-35U-35U575U-34U-34U-74U-75U%75
+M555555555=755=75U=35U=74U=75U=34U=35U=55U=75U-?55U1455555=75
+MU-34U-35U-75U=75U=75U575U]?5U=74U=75U575U=75U=34U-34U=35U=75
+MU=75U=75555555555=55U5755=75U=74UM14U575U=75U=75U=75U=75U-34
+MU-34U=75U554U=74U-75U=74U-74U-75U=75U=75U=55U=55U=75U-35U-35
+MU=75U575U575U-75U=76U5=55%55U=75U=75U=75U=75U=75U575U=555=37
+MU5755575U575U=755=75U=55U5555=55U=35U=75U=55U=755=755=75U=75
+MU-75U=;45U55U=74U-34U-35U-74U=75U=74U=755=555-55U=35U-35U-34
+MU-35U=75U=55U=75U=55U575U5555555U=75U=75U=75U=75U=74UM145575
+M5=75U=75U=74U=35U-75U=35U=75U=74U]55U=55557555545=55U=55U=75
+MU=75U=75U=75U=75U=75U575U57555555=55U=71U%=55-55U=75U=74U-34
+MU-35U=75U575U=755575U=35U=75U-35U=75U=75U=75U555555555155575
+MU-34U-34U=34U=74U=35U=34U-;55U545575U=35U=75U=75U=75U-34U=75
+MU=55U-?5U5555515U555U5755=55U5555=75U=74U=75U=75U=75U=75U=55
+M5555U=75U=74T=175%155=75U=75U=75U=75U=75U=755W-@?W-*1%S6W=H"
+M+2<S"A]WD(2/BK.[O(N.B+2DHJ2`&@UKGX#F'01\[7`T)"$_9;6)$#@A)0.0
+MA/`:!7"7\@0T#)&\I;!K,CT?M+B]A1$?5)V5:P5YC[RSYC$Z,.F\N8X%,300
+ME)5D$]:`M9@"/SP?M[J]RS$_"^*.@N%I7YZ%=0@P#82^N(,(.SP1BK.)=P<<
+MQN)N`@Q$M;^W$3\Z"H:^O)H`-`-=E\43%Y.TM^HU.#/2O:6W$3,S!Y*"G')A
+ME(.:'3$R!(B[OY<V.S''M;>%%1]PE?,%-0V7L[F.`CLY!XN_L?H-#A+GY1<$
+M88"PM'TS.S>>OKB-`3,T:IF:]VG%AHF3`CTS8K&ELV4R/@F1M8GB%F/KDGH.
+M-P:!OKZ:-#H]8+>]B6`"!W3C>@<&]+6]BQH^.`R/N+R2#C8-T)SM;VF6BXKR
+M-#XV[K^ZM04\,@6%B81+:>6%DQ\W,1"UN[WP,SLV[+:QA1,':N;S'@X'G;.\
+M@0D[/ARWN;%7"S49X.IE''V!MHAJ,CD+AKBX@0\]-VV'@>QM1YB#[@TS-M>S
+MNK80/SD,FK:*XAP0]Y1[#34>@KZ]ES$Z,].POXX7"0-G[U8>&^6ULX(!/CX&
+MM;N][C4S#_>$DW!MZXR,1S0\-9R^NHX#/C(9@+2!=!%;DY4<-31AM[NP>ST[
+M-)VSLYH;`ASP^Q8`'IFPLI@U.SQLL;NV9C0W&^B=Q!1TA[2"$C(_#8R[N9@U
+M/S=\C8^6;&26A/@",37CLKJU!#DY`X"SM.<%&G;L<0$.%(R\L_,S.C'OO;Z,
+M&30.8Y3E%!/AB+:$`CX\'+>ZLLPV/0_@@X70;.2&AF4T,@^$N;B`"#@R$8ZV
+M@WX9;>SM$P@)7+>YMQ$_.PN'O+*?!@@%S>)C!1&8L;&5-#@SU+*ZM!$Q,1J0
+MA.UM<9B)AQ@S/02+NK^4-CDWWXB*DFL5Y)S.`#</D;VXB0(X/@2.O;?V``!L
+MX%$:`&&/O;9\,CLWG[^Y@P8V"V*2EWL4YXRUG0\_,FRPI;!E,SP,EXZ`]VK3
+MF9MB-3`!@[FYFC0[,F"ULXUC!Q+VX1<,`ORWOXH:/C@,C;ZRD0TT!LR77!)H
+MF;>T_38Y,>R\NHH:,C`8F8.09&60@Y\%,#`2M+J]\S,X-."TM)P1'$>4VP8U
+M`9Z]OH`).S\<M;^W7@\/%^#R$`5WC+.U%3TX"X:YN8<,,S5GFIG?:?V`B>L.
+M/#%6LJ6V$3P_#9ZUC/T19)23;P@W&(RYO)8Q.C!4MKV-%`(%7.!C`0?BM[V,
+M`3DY!HNXLNP+-@#PG.=K8IR*B$TV/C2=OKN.`SPS'(:)F'EOXH27!38W8K>Z
+MLWX].#60L;:<'P1MX\$:#AJ%LKR:-3L\;K&YM&8*"Q+O[F,<68*VCQP]/@R,
+MN[Z9"STT<8&&Y&_2A8/X#C(TX+VZM`4^/P"&MHCP'A?XE6T/"A:)OK+_,SHQ
+MXK.\@AP(`''O21@9Z+2SA@PY/Q^WNK/,-S`"YH26>V"6CX)A-CP)A+FX@PD^
+M,Q:-M81E%M^2[QHT-5^QN[86/S@(A;*PDP0"$/CS$@,2A+.SES<[,ERSN+46
+M-S0<EY-2%U"!M($%/3P'B[J_ES<_--R,C>AI?9.$V@\P")&\NXD#.#\$C+.*
+MP`086.QD``]@B+^Q<#(Z-Y*\OX`$-`Q^E_,6$.N*MIX)/CUHL:6P>#$R`I6#
+MF$9M[8"':S<R`X.XN84T.#-CB[:&8QEGZ.8>"P_YL;FU&CDX#(*_L)<#"1CS
+MX6D$%82QMO@V.S'CO+N+&#`V'9V%YV]'A8F;!S(R'+2EO?\P/C7GBHB7%&GC
+MG%T--P*8O+F#"3L_'(J]M40#`6?C2`0`=8B]M!4\.PJ'OKZ'`C8)?)WJ817N
+MCK65"S\P1+*EMA8R/0.=B8;3:\N;GA0T,06/N+^1-CLP6;>P@14'$?GE$P\`
+M[K&_C@`Y.0:(N;/B#C4$_Y1U'6*%M[5#,3DWDKZ[CP$],1.%@)1A<IV"D`8P
+M,6ZVNK-S,CD*EK>TEA,=T)1:`#4'A+R_A34[/6BWO[5G#@QH[<\=!=:)LX@?
+M/#D/C;B_F0DS"TB%GUENYH*.Y0L\-^:]NK0:/SP!A+6"V!%REI$7"C<3B+B]
+MY#,Z,>>PLH$2`AO9YF@!!9>VO8`,.#\9M+NPSC4V!N><_!5GF+6.8S$^"X2X
+MN(,//S$4@(Z<9F+HA.@&,31#L;JQ%#\X"9NPMY&+HJ2RB9U!'0`)-3`R/SX^
+M/SPR,3<(#043>\+JG82`C8Z(BXJ*BHN(B8^-@X:%F9*4[/K.779D8F@5%Q$0
+M$Q,3$Q,0$184%6AO8F%D>7-U0EA1T=[$PL[*]?3W]_?W]/7*R<_"P<3;WMW3
+MUM155U%04U)=7%]?7U]<7UQ=4E-04%%65E145=75U-?7U];6UM;6UM;6UM;6
+MUM;7U]?7U]34U-34U-34U-34U=75U=75U=75U-34U-34U=74U-34U-37U-34
+MU-34U-34U-34U-34U-34U-35U-35U-34U-75U=75U=74U=34U-34U-35U=35
+MU-34U-34U-35U=75U=74U-34U-34U-75U=75U=74U-34U=75U=75U=75U=75
+MU=75U=75U=75U=74U-35U=75U=75U=75U-35U-75U=75U=75U=75U-75U=75
+M5=75U=75U=75U=755=55U=75U=75U=75U55555555575U=75U=755=75U=75
+MU=75U=35U=75U=75U=34U-34U=75U=75U=75U=75U=755=555555U=75U=75
+MU5755=55U=75U=75U=75U=75U=75U=35U-75U=75U=75U=75U=75U=75U=75
+M5=55U=75U=75U555U=75U=75U=75U=555=75U=75U-75U=75U=75U=75U=75
+MU=75U55555755=75U=75U575U5755=75U=75U=75U=55U575U=75U=75U=55
+M55555=75U=75U=75U575U=75U=75U=75U=75U=75U=75U=35U=75U5555=55
+MU=75U=75U=75U=75U=75U=75U=755=75U=75U=75U=75U=55U=75U=74U=75
+MU=75U=75U=75U=35U=35U=55U=75U=GID./A]\5<2W%Y>V1D9V5D>'A]<W1+
+M0$525M;2V,7`PL_.R,C+R,C)SLS"P,;%VMG?W=#1U]555U914U-275U=7%)=
+M4E-24U-04%%145=45%55U=34U-?4U]?4U]34U-35U-75555555555=755555
+M5555555555555555557555145%555=555=755555U5555575U=75U=74U-74
+MU]15U5555555555555755575U=75U=75U=75U=755=55U=75557555555555
+M5%155=755=75U=75U=35U=35U=35U=35U-35U-75U-75U%55U=74U-35U575
+MU5755555557555555%55U575U=74U-34U=35U-35U-75U=55U=75U=55U=75
+MU=75U=35U-75U-35U=55U555U575U=75U-?4U575557555555575U=75U=34
+MU-35U=75U=75U=75U=755=555=755=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U57555755575U-555=75U-34U=75U=75U=75U=75U=75U=75U=75U-35
+MU-75U=755=55U=75U=75U=75U=35U=75U-74U-75U=75U575U=75U575U=75
+MU=755=74U=75U=5555555=75U=75U-35U-75U-75U=75U=75U=5555555555
+M55555575U=75U-55U=75U=74U-74U575U=75U=755575U=75U=755535U=35
+MU-34U=555=75U=75U-75U55555555575U=74U=75U=74U=75U=75U=75U=74
+MU5555=75U555U=75U=75U=755=75U=75U=755575U]35U=75555555555=75
+MU=35U-34U-34U-34U-75U=755555U575U=75U=75U=75U=755=75U=74U=75
+MU=74U-34U=35U=35U=35U=75U514U575U-74U-34U-74U=75U=74U-35U=75
+MU-35U=75U=75U=75U=74U-75U=75U575UM5755155=74U=74U=74U-34U-75
+MU=75U5555=55U=34555555155575U=75U=74U-34U=74U=75U=75U=75U=75
+MU=75U=75U=755=75U=31U5=55%55U=75U-35U=75U=75U=75U=75U=75U-55
+M5=75U-34U-35U-75U=75U=55555555555=75U=75U5555575U=75U=75U=75
+MU=75U=;55U145575U=35U=75U-75U=75U=75U=75U=75U=74U%7555555=75
+M55555555U5555=75U=75U=75U=75U5755575U=75U=755=75U=74UM145575
+MU=75U=75U=75U=55U=75U=75U=755=755%155575U=75U=34U=35U=75U-75
+MU5755=75U575U=75U575U=75U575U=75U=75U=36U5=45-75U=75U=75U=75
+MU=75U=75U5755575U=74U]35U57555155555557555555555U5555555U=55
+M5575U=75555555755=755=75U=;55U5455755=75U=75U=35U=75U=75U=55
+M55555517U=75U-75U=75U=75U=75U=75U5555=75U=75U=75U=75U=75U=75
+MU=55U=75U=74UM5755155555U=75U-75U=55U=75U=75U=75U=755=34U=55
+M555555755=75U=755=75U=755=75U-35U=75U=7555755=75U=75U=55U=36
+MU%?5U=35U=55U5555=75U=75U=75U=74U-74U-355=34U-34U-34U=75U=75
+MU=75U=75U=75U=75U=35U-35U=35U-34U-75U-75U-75U-;45%55U=74U-75
+MU575U-36U]355%75U-?4U5545%35U]7555145%75U=75U=75U=5555555575
+MU575U=555555U=75U=75U-35U=75U-34UM575574U-34U=74U-74U=75U=75
+M5=74U-7555755U355=35U=15U=75U-?4U575U=34U-35U=75U-7555145=75
+MU-75U=74U-35U=75U-36U%=45-75U=75U=5?969S<T-%5]'91STC)3,/$\J8
+M@+"TBK2WL*6ELXJVO+#B#0SMO;B&-20["6X;"C0:^60P)B$WAK""!C`T%N1E
+M!6B-O[W@,3\'M+N]G11SG)]H#`6#N+B%-SDTE[2.\!?VA94"/3'QO;N-#C\W
+M?H218&":M(,'/C\0L+NT$30/<>IZ!!6#O;%L/SL-C[RT7082Y?X$-`"&OK^5
+M,#LUD+2,P17GAY4",C>5OKN#"3PUTX>087Z'M(``/CQNL[NU'S0,2.EB!VB-
+MO;<0/CL`B+RU?0$0Y_<&-`:#N;SD,S@+GK2-56KMA^\//32=N;B&"CP*]H:7
+M8G:&M(<-.3USLKB)!30"7>]I!V..O+09.3@$M;R+8`$6X<$!-`6,N+W2/3@.
+MA;2#0FOHA^<)/36:N+B;-3P)Y(:5;$&`M(4..3+'O;B,!S<#V>T5!WB(O(H'
+M.#D>M+R.:0$5X]0#-!F)N+)^/#D"A[2!<VF4A_`+/0B&N+F3-ST/[(;N;E2"
+MM)X+.#/AO+F#`#0!R.,6!TZ*O(D`.#X1M[R,%P9I[48"-1**N[!H/SX!@;2&
+M>FR6A]TU/0^"N[[K-CT-E8?C:<:,M)`U.#&4O[F'`C0'\N80!].TO(\,.SQL
+MMKV#$`9B['$,-1>TN[$3/SX$@[2$9F.0ATPT/0*/NKS[,3T`EH?D:/"/M.HW
+M.#:<OKZ;##0$^^42!/&WO(,).SUPL;V&'`9G[WH/-6RWN[8;/C\8C;2;8F:=
+MA'LW,@&(NKU5,#(&DX?]:.6.M.$V.#2%N;^=#C0;Y_X<!.>VO88U.C+;L+*$
+M&0=Y[V`."GZQN[0'.3P3C[2>;WN?A&TV,@2UNK)X,S(%G(3(:..(M?4Q.#6!
+MN+R4"34>X/8>!>FQO84T.C/GL[*8&P1W[VD("E*PNXH#.3T5B;62:7.9A14Q
+M,AFWI;!H,S,9GH7?:>F+M44P.0B"N+W@"S42XL(8&I>QO9PV.C'JLK.=!01&
+M[!0+"/&SN(D/.#)AB+61:DF;FA`Q,Q"VI;$0,C`2FYI2;I2*BV4S.0^.N[+T
+M"C41[-,:&)*PLI<Q.C:2LK"7!`57[1$*">.RN(T(.#!TBHJ5%5^:F1\P,&BP
+MI;<9/3$6A9M";Y"UB&@R.0.+N[!;-0H4[EX%'IFSL^,S.C28O;'N!QO;XQ(*
+M#I2]N(`U.#'9M8OO%=V%GQHS,'JSNK0$/39HA9EV;9VUCA$]/@:UN[%E-0AI
+M[D@$'86RL\LR.S6'O;;G!AG(X1XU#)*\N80T.#;XM8CF%,Z$D@0S,5.]NHL!
+M/3=FA)]_8)FTC!X\/QJWN[9N-`E@Z7T$$X>RL$X].PB`O+?U!A_PY!LU`IB_
+MOIXV.#?LM([_%/*$D0$R-OR\NHX-/#1PAYUE9YJTC04_/QVVN[06-`YEZ64'
+M%H"]L6`\.`R-O+16!AWY^04U`(2^OY8Q.#67M(_)%>6'E`,R-^^_NX(./#5?
+MAY!F>X2T@`8^/!6PN[4<-`QQ[F`':H*]MA<_.`..O+5V`1/D\`<U!H"YO.TS
+M.`N2M(W3%>"'Z`TR-)&^NX$(/`K"AY9C<X:TA@,^/6>SNX@;-`U![V\';(^\
+MM!P^.0>+O(MD`1'FS@$T!(VYO?0R.0Z9M()::N^'X`XR-9ZYN(4U/`C^AI1M
+M28&TA`PY,EBRN(\$-P/4[&H'9XF\M04Y/ANUO(EO`13@W``T&XZXLDP].0R%
+MM(!T:.J'^0@]"X2XN9PT/0[@ANAO7H.TF`DY,_*\N((!-P#'XA<'<(N\BP8X
+M/A*TO(\5`6KB70(U'(NXLV,\/@.&M(9^;Y>'S`H]#H&[OI<W/0SIA^UNW8VT
+MG0HY,.^_N8$#-`;WX!$'7K6\C@(X/Q6WO8(1!F_M2`TU$;6[L!<_/@:`M(=D
+M;9&'4C4R#(V[O^,V/0.4A^9IR(RTES0X-I&^OH4--`?_YQ,$P+2\C0\[/6:V
+MO8`2!F#L?`PU:[2[MA\^/P6"M(5@8)*'<30R`XZZO/4Q,@&1A_AH_XZT[S<X
+M-YF^OYX/-`7E^QT$_K>\@`L[,D"QLH<?!F7O9PXU8;:[MP4^/!^,M)AM9)R$
+M9C<R!HJZO4\P,@>2A/%HYXFU^#8X-(2YOY`.-!CF\A\%X[:]AS4[,_2PLH48
+M!WWO;0D*=+&[M0$Y/1&.M9QN?IZ%:38R!;2ZLV$S,QJ?A,-H[(BUV3`Y"H"X
+MO.@(-1_C]1D%ZK&]F#<Z,..SLYX:!$KL:P@+W["[BP(Y,FF)M9-H<9B%%S$S
+M'+>EL!0R,!^9A=%IZHJ*=S,Y"8VXLN4+-1/MQ1L;D;"RDS8Z,9>RLY,%!5CM
+M%PL(^;*XCPXX,V6+M9=J0YN;$C$S%K&EMATR,!.;FEYOE[6+8#(Y#8F[L\0U
+M"A;OU1H9G;.RZC`Z-YRRL+.BI+*.DW4?``DU,#T_/CX^/#(Q-P@-!1-YS96=
+MA(",CHB+M8JUBXN)CXV#AH69DI3L^LY==F1B:!47$1`3$Q,3$Q`1%A05:&]B
+M861Y<W5#6%'6WL3"SLKT]_?W]_?T]<K)S\+!Q]K>W-/6U%1645-375U<7%]?
+M7UQ?7%U=4E-045975%555=74U-?7UM;6T=;1T=;6UM;6UM?7U]?4U-34U=74
+MU=75U-35U=75U=75U=75U=34U-74U-34U=34U-34U-34U-34U=75U=35U-34
+MU-34U=35U=75U=74U-34U-35U=75U=75U=34U-75U=75U=75U=74U=74U=75
+MU=75U=35U-34U=74U=75U=75U=74U=35U-35U=75U=75U=75U=75U=75U=75
+MU=35U=75U=75U=75U=74U-35U-75U575U575U=75U=755555U575U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=555=55U=75U=75U=555555
+MU=75U=75U=75U=755=75U=75U=75U=755555U=55U=75U=75U=75U=75U=74
+MU-34U-35U=75U-75U-35U-75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+M5555U555U=75U=75U555U=75U=75U-75U=755=55U=75U=75U57555555555
+MU=75U=75U=75U=75U=75U-35U=75U=75U=75U=75U=75U55555555575U=75
+MU5755575U=75U=75U=75U=75U=75U=75U=75U=755=55U=75U=75U=75U575
+MU=75U=75U=75U=7:ZY'@X?3$7DAP>7MD9&=E9'AX?7-T2T%%4E;1TMO%P\+.
+MR<C(R,G(SL_-PL#&Q-O9W]W0T=#545%045-24E)24E)=4E)24U-04%%64597
+M5%155=755=74U-?7U]?7U]?7U-?4U-34U=755555U555555555555%145514
+M5515U=75U=74UM165%15U=75U=75U=75U=75U=55557555555=755575U-75
+M55755=555555U=75U=75U=75U=75U=74U-75U=5555555555555555755=76
+MU5945%75U=75U=75U=35U=75U=75U575U=555=55U514U575U=75U=75U=75
+MU=75U57555555=755575U=74U-34U=74U-75U=75U=75U-'45U1455755=75
+MU=75U=75U=35U=35U=55U575U=75U-34U=555555U575U=75U=75U=75U=75
+MU=35U-75U575U=74U=75555555555=75UM1655155=75U=75U-74U-75U-75
+MU=75U=75U=75U=545=75U-75U=75U=75U=75U=75U=75U=755=555575U=75
+MU=74U=34U-75U=74U=36U5945%555=75U=75U=75U=755=75U=75U575U555
+M5574U%75555555555=75U=75U=75U=755=75U=35U=75U=555=75U=55U=55
+M55555==55E55U=74U-34U-35U-74U=75U=75U=75U575U=555U355=75U=75
+MU=755=75U=75U=35U=75U=75U=75U=75U=75U=75U=75U=555575U-555=75
+MU=74U-75U=75U=75U=74U=34U-35U=75U=74U]35U=75U=75U=75U=75U=75
+MU-75U=75U-75U-35U=75U=75U=75U=75U=75U=34U%75U=75U-34U-35U=75
+MU=75U575U=755=755575U554U=75U-35U-35U=75U=75U=75U=75U=75U=75
+MU=75U575U=75U=74U-74U-34U-34555555555=75U=75U=75U=75U=75U=75
+MU=75U=75U=?4U=75U=75U=75U=75U=75U=75U-75U=75U555555555755555
+M557555555555U=55U=75U=35U=35U=75U=35U=75U=75U=75U=75U-555=74
+MU-34U-35U=75U=75555555555575U555557555555575U=75U=75U=35U=34
+MU5755=55U=75U=55U=75U-75U-35U575U=75U=755=74U%75555555155555
+M5575U575U=75U=74U=35U=74U-75U=75555555555=555=555%55U=75U=75
+MU575U=75U=75U=75U=75U-75U=755%74U=75U=74U=35U=75U=75U5555555
+M55555%555=75U=75U=755=75U=75U=75U-55U5555575U=75U=755=75U=75
+MU=75U5555575U575U-35U=75U=75U5555555U=75U=74U=755575U=555555
+M5575U=75U=355555U=74U575U=75U=75U=75U=75U5755=75U=75U=35U=75
+M55145575U=75U-75U=75U=75U=55U=75U=75U=75U=75U=75U575U575U=75
+MU=75U=355%5555555555U=75U=75U=75U575U=75U=75U=75U=37U=555515
+M5575U=75U=35U=75U575U575U=75U=75U=35U5555575U=75U=74U-55U=74
+M5=75U=75U=75U=34U-34U-35U=75U=75U=555-75U=75U-34U-34U-35U-55
+MU55555755=755575U=74U=75U=75U=75U=75U-37U%755=75U=35U=75U=75
+MU=75U=75U-74U-35U=75U=74U]34U=75U=755555U=75U-74U-34U-35U=34
+MU=555=75U=755=74U=755=75U=35U=75U-75U-75U-35U-75U=75U=755555
+MU=75U=75U575U-34U-34U-35U=35U=75U=75U=75U=75U=75U575U=75U=75
+MU=75U=75U=55U-5555555=55U=75U-74U-35U=75U=75U=74U=75U575U-?4
+MU=755575U=75U=34U=75U=74U5555=74U=34U-35U=75U=55U=75U575U=74
+MU5555=75U5755555U=75U=74U=74U=74U=75U=75U554U=35U-34U-34U-35
+M55555555U=55U=755555U575U575U=755575U=74U=75U=555555U=74U-34
+MU-75U=75U575U=75U=35U-75U=75U-37U-75U=75U=75U=75U=75U=75U5-Y
+M87)S3$10UM_6-R(D,@D3T)^&L;>*M+:SO+:TN:"F@`P(T+2TRQMO[QXY(#IL
+MCI$U/C$##ST_!;2RE#$^#Y292GB"O[!I/3#NO;*'QIN(GP\R![>EL&`(&^10
+M`@_NO+Z6,#X"DII_%)NVC`TZ//2PMN`?U)C6-#T$MKJT&S<!_,`&`9R_O_HR
+M/@2&@O%AFK>!"3LPD[RQ_P5DD78T,Q&PNH@#,`/X^!H%FKRS9S\^'HR.YF"?
+MBI\U.S>&OK%=`!3O>34V<+VZ@@@R`^SK$!*'O;8=.3]JM;7B;Y&,Z#0Y"X^X
+MMF,/'^5Y"#7ZO+B%-SP`D9UL%8:RM08[/$:VM.(7XH'Q-S\#M+NW$S4%\G$,
+M#Y:_OI4P/P:8A'1BA[&,##LR[;.VY!S#F$`W/1BQNK4$-P;]4`$!F;^\TCT_
+M!8&#QV:$MX<*.S&>O+;#!7Z1>S0P:+.ZCPTP`.?S!1N$O[-K/C\3CX_^89B*
+MDS0X-("^MG$`%>YF-3=7O;N!"C(`Z>X2$(:]MQLY/&"UBN=ODXSC-SD.B;BW
+M:P\=YV8+"NR_N)\V/0:0DFYH@;*+`SL]QK:TY!?N@=XV/P:WN[0?-1K^?@\,
+MG;Z^XS,\!)J%<F&!L(().S"5L[?Q'/>8<C8R';"ZB`8W!_A'``:%OKUW/#\>
+M@(#69(>WA34[-H6\MU`%<9%A-S!GLKJ-#S`&X?0%&8:_L!$^/!2.C_=GFHJ7
+M-S@U@KZW9`!IZ6TT-_:\NX0U,P;J[1T6@+RT!#@]?+6+_VV=C/@V.0V*N+07
+M#Q/G8@L+E+ZYD3$]!Y*0:V^#LHD-.S+YL;7S%.J!138_!+:[BALU&/MG#PV9
+MOK_S,CP:A9IX9("P@`L[,9"RM,0=_)AG-C(6L[J.`#<%Y74#!X>^LF$\/!*`
+M@5YXAK>9-#LWA[RW2P5/D6\W,7>]NX`),0?CP`0?@+^Q'SD];HZ,P&6%BN@V
+M.`B/N;1M`&WI:S0TYK^XF#0S!)3@'!2"O+4!.#);M(CT8I^,R#$^`[6XM1,/
+M$>9I"@B3OK[J,3(%G9$58X*RCPX[,^*QM<P4E(9S,3P8L;N(!0H?Y6`.`X6Y
+MO-0R/1Z$F&1X@["&-3LVG[*T41WEF&TQ,FBRNXT"-QKG<@,$@;FS:C\]%H.&
+M0WV!MYTW.S6!O+1_!5Z1%38QU[R[AP@Q!>W=!!V"OK8:.3UFB8W2>(2*XS$X
+M#HFYM6L`8.D4-#3IOKB=-S,%E^<?:HR\B@,X,\>TB<1@F8S5,#X&M+B*'`\7
+MYA4U#I^YON$P,AB<EQ1FC+*-"#LPZK&+TA66AF0P/!VPNXX'"AWE;`D`A[F]
+M<3T]$H2987*-L(0T.S>;LK5,$N:9:S$S9K*[@PTW&.9[`@6#N;`1/SUJ@H=T
+M=H.WD38["H._M6<%U)$7-C;QO[B%"C$;[U$'$XR^MP<Y,G.(@E!\AXKY,#@-
+MB[F*%`!DZ1$W-9>YN98W,!B6^AYNC[R(##LS_[2/UV:;C$@P/@2WN(@9#Q7A
+M%S4/FKF_\3,R'YZ4%WJ/LH,*.S&1L8A<:I"&;3`]$;"[C`8*$^1K"0&!N+)A
+M/3(1AY]C=(RPFS<[-(2RBG`2XYD7,#-VO;N!#S<?X6<"&(VYL1P^,FV"AW!/
+M@K>5,3L(C;^*8AK;D1`Q-N:_N)DU,1GN1P<6CKZT`3@S6(B#1'&!B\XP.0.U
+MN8@1`7GN$S<*DKF^Z#8P'Y;\&6*)O(X..S#@MXQ>9)J->#,_&[:XCAL,:.81
+M-0R$N+S4,S,2GNL6<HFR@34[-IVQB4]HDX=J,SUKL[B"``L1Y!4(!H.XLFL\
+M,Q6'G&U`C["?-CLUAK*(>Q/OGA`P,%6\N(0.-!WA8PT>C[BQ&SXS9(V$?T6-
+MM^TP.`Z/OXAI&LZ6'3$WZ;ZYDC4V'.E*!A2)OK4#.##%BX!)2H&+43,Y`;2^
+MB1(!<.\<-@N9N+[A,3`=D?<99HB\C`@[,>FWC4UZA8U@,C\<L;B,!0QOYA(U
+M#8:XO'<R,Q&9Z1%*B+*'-#LWF;".<6Z=AQ8R,F"RN(`#"Q?G%P@'C;BS$3PS
+M;H:2;%.)L9`Q.PJ`LHEF$.B?'3,P][^XF@DT$^!O#1R)N+8$/C!QC85[5HRW
+MY3,X#(F_CA4;\9<>,326N;F6-#83Z',&:XN^B@PX,/.+@79&@(AU,CD'M[Z/
+M'`%*[!XV"(6XO_`Q,1"0S1AXBKR""CLVE[>"=WZ$@F@R/Q"PN8($#&/F'#0"
+M@[N]9S(P%)CM$5J+LH4W.S2:L(Q^;)R'$S(R?;VXAP((%><0";6IH+NQ@^YC
+M&@(+,3,_/SD^/STS-C4.`1EI0>&1FX:"C(F(BHJ*BHN)CHR"@8>;G)'KX?#2
+M3'YA;VH4%A$0$Q,3$Q`1%A<5:VYM8&=[?79.1%+5W=K!S,C*]/?W]_?T]/7(
+MSLW`Q]K8W-+1U]545E%34EU<7%]?7UQ<75U24E-04%%65U15U=34U-?7UM;6
+MUM;6T='1T=;6UM;7U]?7U]37U-34U-34U=75U=75U=75U=75U=75U=75U=74
+MU-34U-35U-74U-34U-34U-34U-75U=34U-34U-74U-35U=75U=35U-34U-35
+MU-74U-34U-34U-34U-35U=75U=34U-34U-35U=75U=75U=74U=75U=75U=75
+MU=74U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=755=75U=75U=75
+MU=75U=755=75U=75U=75U=555=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=5555555575U=75U=75U575U=75U=74U=74U=75U=75U=75
+MU=74U=75U=75U=75U=34U=75U=75U=75U=75U=75U=75555555755=75U555
+MU55555555575U=75U=7555755575U=75U=75U=75U=75U=74U=75U=75U=75
+MU=75U=75U=75555555755=75U=75U=55U575U=75U=75U=755=755=55U=75
+MU=75U5555555U575U=75U=755=75U=75U=75U=75U=75U=75U=74U-75U=75
+MU=75U=75U=75U=7555555555U=75U=75U=55U=75U=7#E);AYLG:6DAS>7ID
+M9&=E97YY?'-T2T9:4U?0W=O$P\+.SLO(R\C+R<[,PL#&Q=O9W-/3T-=55%91
+M4%-24EU=75U=4E)34U!045%65E=45%155=75U=34U-?4U-?4U-?7T-=4U575
+MU=75U=75555555155514551455155=555=755%=5U=75U=75U=75U=75U=75
+MU575555555555555U=75U=75U=75U=55U575U=36U5945%555=75U=74U=34
+MU=75U=75U=75U55555555555U=?5555555755=555=75U=75U=75U=75U=55
+MU=55551555555555U555U=75U=;55U54U575U-74U=75U=74U=75U=75U=35
+MU-34U=75U554U575U-34U-35U=75U=75U=75U=75U=75U5555555U575U=74
+MU-75U-75U575U=55U=75U=74U=755=75U=75U555U575U-75U-75U=755=74
+MU-755=5555555555U575U575U=75U-75U-35U-75U=75U=75U=75U=755=74
+MU5755=75U=755=75U=75U=35U=75U=75U555U555U5555535U=75U=75U=75
+MU=75U=5555555555U555U574U=74U=74U=75U=75U=34U=?45=55U=75U=75
+MU=75U=75U=75U575U=75U=75U=75U575U]75U57555555555U=55U5555=75
+M5=75U=34U=35U=75U=75U=55U5555=75U-55U555U=75U=75U=75U=75U=75
+MU=74U=75U-75U=755515U=75U=75U=75U-34U-34U-34U=75U=75U-75U=35
+MU=755=75U=55U=75U=74U5555=55U=75U=75U=75U-75U=75U=75U=75U=74
+MU=35U-?4U=75U555U=55U=75U-75U=75U=75U=35U-34U-74U-35U=75U=75
+MU=75U=355=75U-74U-74U-75U=74U=75U=74U=75U=35U-7555545=55U=75
+MU-34U=75U=35U-75U-35U-75U=75U=75U-74U=75U=75U555U=75U-75U=75
+MU=75U=75U=75U=75U-34U=74U=35U=75U=755574U-5555555=555=555555
+MU575U=75U-74U=75U=75U=75U-35U=75U=755=74U5755=74U-34U=75U=55
+MU=755=75U=35U=55U55555555%55U=75U=34U-75U=75U=75U=75U=75U=75
+M5=75U=74U=75U=75U=75U=75U=35U=55U=755=7555555=75U=74U-75U=75
+MU=55U=75U575U]35U575555555155575U=75U=75U575U=75U-75U=75U=35
+MU=755=755=75U-75U=75U=75U-75U=75U55555555555557555555575U514
+M5575U=74U-74U-35U=34U=755=75U=755=75U=75U=75U=75U=75U-35U=34
+MU5555%55555555755555U555U=75U=75U-75U=75U=75U=?4U5555%155555
+M5575U=75U=34U-75U=555=555%555555555555555575U=755555U555U=75
+MU=75U=75U=75U=74U=75U575555555545-75U=34U-35U=35U=35U=755=75
+MU=75U=75U=75U=75U=75U=55U555U575U-55U=75U=75U=75U=75U=75U=75
+MU=755=75U=555=755=74U-75U=55U=555=55U=75U=75U=75U=75U=34U=75
+MU=75U=75U=75U575U=75U5755=75U=75U=35U=75U=74U=75U=75U=75U-74
+MU=355=75U-34U-34U-34U=75U-35U=75U55555555575U=74U=75U-34U-34
+MU=75U-34U=34U-35U=75U=75U=75U=34U=75U=75U=75U=74U=74U]?5U=75
+MU=75U=75U=75U=34U-34U-74U=75U575U555U=75U5755=555=75UM575%35
+MU=35U=75U-75U=75U-75U=35U-75U=35U=75U555U=74U-34U-75U=74U=75
+MU=75U=75U=7555555=75U=75U=74U=755575U=75U555U555U=55U=75U=35
+MU=75U=75U=74U-35U-75U-75U=34U=5555555=75U=74U=34U=34U-34U=75
+MU=35U=555555U=55U=555=55U=555%55U=75U-75U=75U575U=75U=755=75
+MU=755=75U=545=74U=34U-35U=75U=75U=75U=75U=75U=75U=75U=74U=75
+MU=75U=55U575U555U=75U575U=75U=75U=35U=75U=75U=74U=75U=555=75
+MU-7555755=75U=555575U=75U=75U=75U=35U555U575U=75U=75U555U=74
+MU575U=74U=35U=7555555=75U=35U=34U-34U-35U=7555=4U=74U=34U-74
+MU-35U-35U=75U=75U=75U555U5555575U=75U=75U=75U=355=755=75U=75
+MU=75U=75U=555=75U=75U=75U-35U=75U-?4U=75555555555=55U=55U555
+M5575U=75U=75U=75U=34U=75U=755575U-555=75U=35U=35U=75U=75U=35
+MU=75U=75U-75U575U554U=34U-34U-34U=34U=35U=75U575555555555=75
+MU=75U=75U=75U=74U-74U5755=75U=55U=55U=75U-74U=75U=75U=74U=75
+MU=755=37U=75U575U=75U=75U-755=75U-35U=75U=75U=74U=75U5755=55
+MU=75U=35U=55U-75U=75U=75U575U-75U=75U=75U=75U=74U-355=34U=75
+MU=75U=155555U55555555=75U575U=75U=35U=75U=74U-35U575U-35U=75
+M5555U=75U5555=75U=75U=75U=75U=34U=345=75U]75U=75U=75U=75U=35
+M6F=G<'!!1531V'P[(#@P#!;SGH"TM;>WL+*]M[>]IZ>W&S4;B;NQ'3@E-VAX
+M`#0*&@4V)24(@;:=,2<Y8[>QGQH$3)WM:FV&O;^:-3T&MZ2Y[C4T:8"-E7SF
+M@8<6-C%CL+N)"3H]0;>VE@<!8^5B``>6L;#G,SL+C[NR6#8V$YN$]V/B@H80
+M,3!FL[J+##@SW+>VD!L;1.-I`@:3L[/@,SH*C+BS1C<W$)R<0V_JCX`=,S-B
+ML[J*##@ST;2TEQX2].T6#@"=LK+B,SH*C;FS6C0U%Y.79&^0B(,<,C)LL[J*
+M#3DP5+6UZQT5X.P="P.<O;WL,SH*@KFP6S4+:I'B:&Z?M8T?/3UNL[J*`CXQ
+M4XN(XA!FE>X8-0V?O+SN,SH*@[ZQ6@L.;93W$6F:MX\9/#QKL[J*`S\V78F,
+MY1=&DN@%-P^>O[_K,SH*@+^V6@X-9.A''FN$MHD9/SP5L[J*`#PW7(R#]VKT
+MF^H'-@Z9O[^5,SL+@;RW10P`<>UG!16&L(L8/S\4L[J+`#TT7(*'TF_BAY0!
+M,0B9OKZ4,SL+AK*T1P($7^%J!A2`LXH8/C\7L[N+`3(U4X&;0F"7@)<#,PN9
+MN;Z4,SL+A[.U0P$9P/H3`Q>"LK48/CX6L+N(!C`*5H>2?F6<C9$-,C68N;Z7
+M,#@(A+"(3`<0^?(9#!:,O+08.3X1L+B(!S$)U)KJ8'*:B9`//369N;F6,#D)
+MA;:.=1INX\H$"1".O[08.3X0L;B)!#8/W9_E:TJ&BI(./#29N;F1,3X.F+>-
+M=A]]E<0!"A*)OK<8.3X0L;F.!30"VI#2%D2#M)P(/S>9N+F0,3X/GK6`<Q#?
+MD=`"-1V(N;89.3X3MKZ/&S4!PI1\$E&,MYX+/C>>N+F0-C\,G(N'?Q7DG%`/
+M-Q^+N+89.3X2M[^-&0L$].UN&=*)L9@*.3:?N;F0-SP-DHZ;>FV5FUL)-AF*
+MN+$>.3X2M+V"'@\8\OH1!<2+L)LU.#:<N;Z3-S("DXV=9WB2A$,*,1NUN[$?
+M.3\2M+*`'`(3^,D>!\VULX4U.#&=N;Z0-#,`D8"48$.:@4@U,`6UNK$</C\2
+MM;.!$@$5YU,$`<NTLH0U.S&2OK^0-3`&EX?@;=B&@W4W,@2UNK`=/CP2B[&'
+M$`1GXW8!`O2VO8<U.S&3OK^0"C$'E)CU:?V#C'<V/0>UNK`2/SP2B;:%$1E!
+M[F0-#/>QO(8T.S&1O[R0"#<%ZI)!%>&/CG8Q/`>UNK`3/ST2C[29%Q/TZFP.
+M#O:QOX8T.C"6O+V1#C09Z)1G%^Z(BW8Q/`:UNK`1/#(3C+6=%!7BEQ4+"/:P
+MOH$T.C"4O+V6#`H=[N,5$92UM78P/P&UNK`6/3,0@HB6:F:7D!8U"O>SN8`U
+M.C&5O;.7#0X6[/<2$Y&WM'0S/@"UNK`7,C,1@(SH:$^?G1,W-?6RN(,U.S'K
+MLK"4`PUHXE\8')*VMW4R.0.UNK`4,S$6AH/F;L*%GAPV-,ZRN(,U.S'NL['J
+M`0!FX'@''I^QMDXR.0.UNK$5,#84A(?W;.>!FQDQ-\"]NX(U.S'ML;;I!P1T
+MX6\`&YBSL4`].`**N[%K,3<5FIM18NN"A1LP-MN]NX(*.#;CMK?M!1G<X1<-
+M!9JRL%H].`V+N+%I-C1IF9-R8Y&.AP4R,=*]NXT+.#;FM[7A&!#^YAT.!X6]
+MLUT].`V(N+9O-PIMG>IC8)^+A@0],%6]NXT+.3?DM(OZ'&[LYA@+!H2\LE0]
+M.PV)N;=L-`EFD.458)JU@`<\,UR]NHT(/C?ZBH[S$WR7X00U`(>_LM(].PV.
+MOK=B"@QXE-P38(>W@@<_,T2]NHT)/S3XB(+"%]*=X08W`X:^O=H].`R/O[1C
+M"`-WZ'(>8(&QC08_,DZ]NXT./#7_CX'7:^6:X`,V`H:YO,(].`R,O+5A#P9>
+MXFP%8X*PCP$^/7>]NXT,/0K]@H5`8NN'X@TP#(:YO/4].`V"O8MF`AK8Y!8&
+M8HRSC@$Y/7*]NXT-,@CS@)]R9).`[`\S#X&XO/$R.`V#LXEF`!WQ\A\#;(Z]
+MB0$Y/'ZRN(T",P[PAY9G<IB-[PDR"8:XO_TR.0V`L(]G!Q3DS`4,;XB\BP$X
+M/&6RN((#,0SPFNUM38>.Z`@]"(:XO_DR/@*&MHUG&F#LT`8.:8J_B@$X/&>S
+MN8(`-@+SG_%J5H"+Z@H\"X:[O^4S/@*'MX!G'TR56`,+:K6^M0$X//VNK*>\
+MBIO%$`8.-3`R/C\Y/S\R,S<U`@<08,KOG(6`C8Z(BHJUBHJ(B8^-@(:%F9*7
+M[^7)4'=E8FD5%Q$0$Q,3$Q,0$184%6AO8F%D>7-T0EA1UM[$PL[*]/3W]_?W
+M]/7*R<_"P<?;V=S3T=155U%04U)=7%]?7U]<7%U=4E-04%%65U=45=75U-?7
+MU];6UM;6UM;6UM'6UM;6U]?7U]34U-34U-34U-35U=75U=75U=74U=35U=75
+MU=75U=34U-34U-34U-34U-34U-34U-34U-35U=35U-74U-34U=75U=75U=75
+MU-34U-34U-75U=75U=74U-74U=34U=74U=75U-34U-34U-75U=75U=34U-34
+MU-75U=75U=75U=35U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=555=55U=55U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U575U=75U=35U=35U=75U=75U=75U=75U=75
+M5=55U=75U=75U=75U=75U=75U=74U=75U=75U=75U=75U=75U=75U=55U=75
+MU=74U=75U=755=75U=75U=75U=755=755=75U=75U=75U=75U=75U=75U-75
+MU=75U5555575U=75U=75U55555555=75U=75U=75U575U=75U=75U=75U=55
+MU=75U=75U=75U55555555575U=75U=75U575U=75U=75U=75U=75U=75U=75
+MU=75U=755555U575U=75U=75U575U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U/*1E>;EQM]-2WQY
+M965D9&5E>'YS=W5*05I35-/?VL?"S,G)R\O*R,C.S\W#P<?%V][?TM#6U%57
+M5E934E)=75Q=75U24U)34%!145%75U=55574U=34U-34U-34U-34U-34U=36
+MU%=55=75U=74U55555545%1555545=5555555555555555545-55U=75U-35
+MU-75U=75555555555=75555555555=75U=75U-75U=55U-;55E54U555U=75
+MU=75U=555555U55555555=75U=55U=555=37U=75U=755=755=75U=74U=35
+MU-34U-35U=55U=55555455555555U=75UM565515U=74U-35U=35U=75U=74
+MU=35U=75U=75U=75U=755=75U=34U-34U=75U=75U=755=75U=75U=75U575
+MU=75U=55U=75U=55U=71U%945%55U=74U=74U=34U=74U-75U=75U=75U=75
+MU=755575U-75U=755=7555555555U=55U=75U=75U-74U=75U575U575U=75
+MU=75U-;45U145575U=75U=75U=755=75U=75U=34U=74U-75U=755515U=75
+MU=75U=35U=55555555555575U575U575U=74U-74U-34U=74U=74T=145535
+M5=75U=75U=75U=55U=75U=75U=75U=75U575U=35U=?4U=75U555U5555515
+MU=75U=75U=755=75U=75U=755575U=75U=75U=36U%=45=35U=75U=355=75
+MU=34U=55U=75U=55U=755575U-55U=75U-34U=34U-34U-74U-75U5555=75
+MU=555=755=75U=75U=75U=55U=;55U54U575U=75U=35U=75U-75U=74U=35
+MU=75U=75U=35U=77U]74U=55U=5555555=75U=75U=34U=34U=34U-75U=75
+MU-75U=75U-75U-35U574U=74U-34U-75U=75U=75U=75U=75U=75U=75U=34
+M5575U=34U=75U=75U=75U=75U=75U=75U=555%75U=75U=75U-75U=75U=71
+MU%=55555U555U=75U=75U=74U=75U=75U=75U=75U=75U=75U]1555555=55
+M5=755=75U=5555555555U=75U-35U-75U=35U-75U=75U=;45U55U5755=75
+MU=75U=75U=5555555=75U=75U=35U=34U%75U=75U=75U=75U=75U-75U=35
+MU=75U=75U=74U-75U-75U575U575U574UM565%355=75U=75U=755=74U=75
+MU=75U=75U=75U=55U=55U=34U=55U=75U=755575U=74U=75U=75U=75U-75
+MU=75U=755=75U=75U=31U%=55-55U=35U-35U=75U=75U=75U=75U=55U555
+MU555U5545=75U-74U-755555U=75U=75U55555555%155%55U=75U575U=75
+MU=75U-;55E145555U=55U=75U575U=75U=75U=35U=55U55555755574U%55
+M5%555%555%755=55U=75U=75U=75U=35U=35U=755=755=75U=75UM165555
+MU=75U=55U575U=75U=74U575U=75U575U=555=755515U=75U=75U=34U=35
+MU=755=75U=75U=55U575U575U=75U=75U=75U=76U5=45%555=75U=75U=35
+MU-75U=75U=74U=75U=755=75U=74U]?5U55555555=75U=75U=75U=75U=75
+MU=75U=35U=75U=75U=75U=75U-;45U55U575U=74U=35U-34U=74U-74U=74
+MU=75U=34U=34U-54U=74U-34U-35U-35U=75U=75U=75U555U5555575U=55
+M5575U=75U574UM175535U=75U=74U-34U-35U-75U=75U=75U=75U-74U-35
+MU=34U=75U=75U-75U=35U-34U-34U=75U=75U=75U5555575U=75U=75U=36
+MU5=55-75U=75U=75U=74U-34U-35U=34U=35U=35U=75U=545-75U-34U-34
+MU-75U-75U=75U=75U=74U=55U=75U=75U=74U=75U=75U=;55E145575U=75
+MU=35U-34U-35U-75U=75U575U=55U575U=74U]35U=55555555555575U=74
+MU-77U-34U=34U=74U=75U=75U=75U=75UM5655355=75U-35U-74U-35U=75
+MU-35U=74U=35U-75U=755%35U=74U=74U-75U=75U=75U=75U=75U=75U=75
+MU=34U=75U=75U=75U=36U%=55=555=555555U=75U575U=75U575U=75U=75
+MU=75U575U-355575557555755=75U=35U-35U-35U=35U-35U=75U=75U-34
+MU-74U-'45U55U575U-34U-35U-75U=75U=75U=34U=75U=74U=74U-54U=75
+MU-34U-35U-35U=75U=75U5555=555555U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=55U=55U575U=75U=75U=75U=75U=755=37U=5555555=75U=55
+MU=75U-75U-74U-74U=75U=755575U575U=75U=75U5755575U=75U=35U=75
+MU=75U=74U=74U=75U=75U=75U=555-75U=34U-35U=75U=75U=75U=75U575
+MU575U555U=75U=75U=75U=75U=;55E54U=75U=74U=35U-34U=75U=34U=75
+MU=75U=35U=75U=74U]75U=75U575U5555555U=75U=75U=74U=74U=74U=75
+MU=75U=75U=75UM5755355=75U=75U575U=74U-34U-74U-75U=75U=75U=75
+M5535U=74U-34U-34U-74U5755=755=5555555555U=755=75U=75U=755=76
+MU5=45=75U-75U-75U=75U=75U=75U=75U=75U-74U=75U-75U-?4U%555555
+M5555U=555=755=75U=755=555555U=75U4!F9'!Q1EK4T=IL)"`_,0(5Y9F#
+MBK6QL;RRMHNWOJ&FMQ@T#9Z_OY<S)B0(E)\7,3X]"P8""@@?[)$3-C\*F+^Y
+MAPHY-I^XI+<4-0SAM;>`]$;KFY47`1.$L+#J-S@VD;ZX@`H[/6VTLX,?"@UF
+ME.-A%?Z!@LH.,`^;O[Z%-#HR_[R[B`8R-FF"BYMM'7GL^Q,#&9VVMNLT.3:6
+MOKN-"#L]8+>]CQ,*#V"3D5EIPX6`_0TQ#)^]OX4T.CW`O;N+!CTP%X*UA&T;
+M:OCG:P8>EK2WE34^-I6_NX\..SQNMK^($#0+:)V9\VQ$G(?Z`#<,D[*_A#4Z
+M/56]NHH'/#,3C;>`8P02QN-E&ASKBK27"#PV[KR[C@\[/Q6VOHH6-S46GX3L
+M8WB4A><'-`V6L[R'"SL]0KVZM00_,AR,MHUF`1I#[%D=$N..M98./3?@O;N)
+M##L_%K:YM!<V-Q*9@)9F;>>?X!L+`I6QO8<).#UPLKJT!3\\&(RPCGH#!F7I
+M\!40_H**D0TR-^6RNXD-.SX3MKBW%3$V&9B-GG@5WI'C'0X`[+:RA@\Y/7JR
+MI;<:/C\%C[.+<@T";NOB9A;"@8F0`S`T\+.XB`,[/ARVN[9H,3`%FXZ$<1!_
+MZ>T7`@'FM+.&#3XR8;.EMQ@^/P>/LK5(#P\6ZI=;%5V%C)`&,33-L+B+`#@^
+M&;>[L6TP,P>;BX%''&CE[6T!!O^*L($#/S)LL*6V'SX^`8^\MUT)"QV5G?)O
+M=IR#D`4W-="QN8L&.#X;M[JP9C,]`9NUC=`8$][M?`4$R8FQ@0$\,FBQNK8=
+M/CD`C[^VV0LU&Y6;[F9EE(>0&30*6;:^B@<Y.06WNK-[,SP"F[>.RP4;<>)1
+M'!K0C+>!!S(S%;:ZL1`^.`*,O['W"C<$ZH>0?6+FFY$3"@M)M+^*!3XY!+2Z
+MLW<S/PR;MHKY!P=BX_`7'EJ`M(8%,S,7MKJQ%SXX#(R^L_LU,0'K@)A$:-J2
+MEA0."7.UO(H8/SX'M;JR13,^#IBQM.`&`Q?AX&`2=(>*AADP,!:TN[!J/SL/
+MC;FRX#4P`NF"A]H4<Y67;`T/>(B]BA\\/@:UNKW3,CD(F;"WZ`$,'.>50Q9^
+MF(F''38Q$+6XL&P_.PZ"N;WI-#,/[X^#_1%OYY5[`0UGC[.*$ST^`8NZO,@S
+M.`J>L[&7``@:^Y#Q:623C806-S83BKBP83P["(.XO)0T/0GBB8S@$Q'9Z4,%
+M`&&"L(L6,C\`B+J\_S,X-9RRLY,#-0;SG.QG8.N!A6HU-Q*(N;!X/3L+@+B_
+MD30\"N&+B.H='G+MVQ\&8H&VB&HS/`".N[SF,SLTG;VRGP(T`\N;D75MY82;
+M8@@T'8Z_L'0].PJ!N+Z=-#\UY;6UD1P$;N?]%P5LA+>);#$\`XR[O^TS.S>3
+MO;V;`C8,WH29WF[9GYEZ#S4<C;RP6#([-8:XOIXT/C?]M+><'@$0_^%C&6^8
+MM8YG-CT#C;B_ZS`Z-I&\O(0",0E<AH?Y:'&7G70""QR#O;'2,S@UA[BYFS0Y
+M-LNWMIL>`AG)Z4X3;I*(CWTT,@.#N;^4,#HQE+R^A@(P"DJ`@.QK8N&04`8)
+M'(:SL<DP.#6%N+B$-#DQWK>PA!D/!-64]!5IE(R-3C4S`H&^OY$Q.C'KO+F`
+M`C(T?H.,EFH7QI3!&@P<A+"V_38Y-)NYN(<U.#!<MK*!&0@`3I'B9FGC@(-0
+M"#`#A[^_DS8Z,.^\N8("/3=FC8B<:AUR[_,=`QR8MK;E-SXTGKFX@34[,TJV
+MO8(9-0QXDI9,;O.$@=H/-@.%O+R<-SHPX;RXC`(\,6^,M9IK&V[DY10&'9VW
+MM^$T/S2=OKB`"CLR?[&\C!XT"6.?G\YO5)Z']0(W`YB]O)XT.S/[O+N.`SPP
+M%8^WAV@'$/7@8042EK6T[34\-)&_N(,+.SUGL;^)'S<U:YF$X6USEIK\`34`
+MG+.]F#0[,_&\NX@`/S,1CK:`;@$84^Q.'!/HB;7I"#TTE+RXC0@[/6VQOHL=
+M-C06FX&48&/CG_H%"P"0L+V:-3@SQ+VZB@$^,A*.L(UM`@=RZ<D7$>&,B^H/
+M,C7HO;B,#CL\:[&YM1(Q-A*%C9UG%<R0YQX.`92VLH4(.3-6O;JU!CX]'XFS
+MB6`,`V#JX&`7\X".E0TS->VRN(\/.S\4L;BT$#`Q'H6.FG@0=NO@$0T&Z;>S
+MA0X^,TVRNK0'/CP;B;*+9`X/%9643A7=A(R4`#$UY[.YC@TX/Q&QN[<6,S,%
+MA(N&<1QOY^-H`0?@M;"$##\SXZZLI[^UF\81!@XU,#(^/CD_/S(S-S4-!Q!C
+MR>R<A8"-CHF*BK6*BHB)CXV#AH28DI?OY<A1=V5C:147$1`3$Q,3$Q`1%A05
+M:&]B861Y<W1"6%'1WL3"SLOU]/?W]_?T]<K)S\+!Q-O>W-/1U]5745!375Q<
+M7U]?7%Q=75U24U!045975U15U=34U-?7U]?6UM;6UM;1T=;6UM;7U]?7U]?4
+MU-34U-35U=75U=75U=75U=75U=75U=75U=34U-34U-34U-74U-34U-34U-34
+MU-75U-74U-34U-35U-75U=75U=34U-34U-75U=75U=75U-34U-34U=75U=75
+MU=74U-75U=75U=75U=75U=35U-75U=75U=75U=34U-34U-35U=75U=75U-75
+MU=75U=75U=75U=75U=75U=75U=75U=75U-75U=75U=75U=75U=75U=75U555
+M5555U=75U=75U=755=55U575U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU-75U=75U=75U=75U=75U=75U=555=75U=75U=75U=75U=75U=75U=75U=75
+MU=55U=75U=75U=75U=5555555555U=75U=55555555555=75U575U=555555
+MU=75U=75U=75U=75U=75U=75U=35U=75U=75U=75U=75U=7555755575U=75
+MU=75U555U=75U=75U=75U=75U=75U=75U=75U=75U55555555=55U=555=55
+M5555U=75U=75U=75U=75U=75U-74U=35U=75U=75U=75U=75U=755555U575
+MU=75U=75U=75U=75U=?ZD.CA^\333G5^>61E9V1E>GA_<G9U3$1>4=33WL7&
+MPLS.R,O(R\O+R<S"P\;$VMG?W=/1U]555U914%)34EU24EU24E)34U!14596
+M5E=45-755=75U=75U-34U-34U-34U=35U=75U=75U=55557555555%545U14
+M55555=75U=75U-75U=755=755555555555755=75557555755=75U=555555
+M5=755=755=555=755=75U=75U=75U=74U=75U=75U=74U]35557555555555
+M555555555=755=75U=75U=55U=75U=75U=75557555155-55U=75U=75U=75
+MU=75U=75U=75U=75U=75U=55U=74U575U=34U-34U-35U-35U=75U=755555
+M5555U575U=75U=75U=35U=35U-?55=75U=75U=75U=75U=75U=75U=74U-75
+MU575U575U=555555U=35U575U=75U=75U=75U=75U=75U=74U-75U=34U=75
+MU=5555555=75U-55U575U=75U=35U=75U=755=755=75U=75U=74U=74U=75
+MU=545=75U=75U=35U=75U=75U=75U55555755555U=75U-75U-75U=75U=74
+MU5755=74U-755=755=555=755=75U=75U=55U=35U=75U=755=74U5555555
+M55755=55557555555=75U575U=74U=75U=75U575U=75U=3555555575U=75
+MU=755=75U=75U=755=75U=75U=75U=75U=755575U=34U-34U-74U-74U-74
+MU=75U=7555555555U=55U=75U=74U=75U-555=75U=75U=75U=75U=555=55
+MU=75U=75U57555555575U575U-75U5555=55U=75U=75U=75U=75U=34U=74
+MU=75U=75U=75U=75U=74U-75U=74U=34U-34U-35U=75U=75U575U5555575
+M5=7555755515U=74U=74U-34U=75U=555555U=55U=75U=75U575U=75U=75
+MU-75U=34U=75U-35U=75U=75U-74U=75U=74U-75U=75U575U=75U=75U=34
+MU=75U=755=75U=555=5555555575U=55U=75U=55U55555555=74U-75U=74
+MU-75U=75U=75U575U=75U=75U=35U=755=755=755=545=75U=75U=75U-34
+MU-75U=75U=755=55U=75U=75U=75U=75U=75U-74U535U=75U=35U=755=75
+MU-75U=75U=75U=75U=75U=55U=55U=74U]75U555U575U=75U=75U-75U=75
+MU=75U=7555555555555555755=755%155575U=75U=75U=75U=75U=35U=75
+MU=74U=75U-75U=755515U=74U-74U-75U=75U=75U5555555555555155555
+M5555U=755=75U=54555555555555U=7555555555555555555=55U=75U=75
+MU=75U]35U=755555U555557555755=75U=75U=75U=75U=75U575U=75U=74
+MU5555555U555U=75U-35U=75U=555555U555U=75U=75U=75U5145=75U=74
+MU=74U=75U=7555555575U=75U=75U=75U=75U=75U=75U-'45U545575U=75
+MU=75U=75U=55U=75U=75U=75U=755575U=75U=34U=555555U=75U=75U=35
+MU=75U=75U=75U=35U=75U=75U=75U=75UM5655155=75U=75U-74U-34U-74
+MU=75U575U=75U=555=7555545-75U-34U-35U=55U=75U=75U=75U=55U=55
+MU=75U=75U=74U=75U=76U5945%55U=75U=75U=75U5755=75U=74U=75U=75
+MU=75U=75U=74U]755=55U555U575U=75U=75U-35U-75U-74U-75U-74U-74
+MU=34U-;45U55U=74U-34U-75U-75U=75U=75U=75U=55U=55U=35U=755515
+MU=75U=75U-75U=75U-55U57555555555U5555=75U=75U=75U=75UM565535
+MU=75U=75U=75U=755=755=75U=75U=35U-75U=75U=75U]?4U=75U=75U=75
+MU=74U=74U=75U=75U=75U-74U=75U=75U=75U=36U%=45-75U=75U=34U-35
+MU-74U-35U=555=555=55U=755575U514U=74U-74U-75U=75U=75U=75U=75
+MU575U=75U=75U=75U=75U=75U-;45E54U=75U=75U=75U=55U=75U=75U=75
+MU555U575U=74U=75U=37U555555555555555U555U=75U=755=75U=34U=35
+MU=75U=755575UM565%=5U=75U-75U-34U-35U-75U-35U-755=74U=75U=75
+MU-155=75U-34U-34U-35U=755=75U=75U=75U=555555U=75U-34U-34U-31
+MU%=55=55U=755575U=75U575U555U=75U=75U-34U=35U=75U=74U-75U=75
+MU=75U555U5555=55U=75U=75U=74U=755=75U=75U=75U-;45U54U=75U=75
+MU=34U=34U-35U=755575U=75U=75U=555=75U555U-34U-34U-74U=75U=75
+MU=75557555555555U=7555555=755575UM575%355=555555U=75U=75U=34
+MU-15U=75U=75U=75U=75U575U=?5U575U5555=75U575U=75U=74U=75U=75
+MU=74U=75U=55U=75U=36U%=45%55U=75U-75U=755=75U=75U-35U-75U-75
+MU=35U-75U-54U=35U-34U-74U=75U=75U=75U=75U=75U=75U=74U-75U=74
+MU=74U-'45U55U=75U-75U=75U5555=55U575U=75U=75U-75U=74U-34U=37
+MU-75U=5555555575U=75U=74U=35U=35U=35U-34U-35U=75U=74T=175535
+MU=74U=75U=75U=75U=75U-75U-74U-35U=75U=55U=355=74U-34U-34U-34
+MU=75U=75U=555=755=555555U=75U=75U=75U=76U5955%55U=75U=75U=75
+MU=75U=74U-74U=74U=75U=75U=755=74U-355=75U=755=75U=75U=75U=55
+MU=75U-75U=75U=75U=75U=75U=355-55U=75U=74U-34U-74U=34U=34U-35
+MU=745=75U=75U=74U%55U-34U-35U=75U=75U=55U=755=7555555575U=75
+MU=75U575U575UM565%15U=75U=74U-75U=75U=74U=75U=755=75U=55U=75
+MU=55U-?5U=55U555U=75U=75U=75U=35U-35U=35U-5555755=75U=75U=31
+MU%=55-75U=34U-75U=75U=75U=35U-74U-75U-34U=34U=75U=54U=35U-75
+MU-75U-35U=75U=75U=75U=75U-75U=75U=75U=74U-34U-'75]55U=75U=35
+MU5555=75U=75U=35U=55U=75U=75U=75U=75U=77U-74U=75U=7555555=75
+MU=355555U=34U-35U=75U=35U=75U=34T=175535U=75U=75U=75U=74U-34
+MU=75U575U575U=75U=75U=755%74U-34U-34U=75U=75U=75U57555555555
+M5=55U=75U=555=555=76U5=55-75U=74U-75U=75U-75U=75U=7555555575
+M5=75U=755=74U]35U575U55555754']@?7-.1%/1W-PU(B0R"!)<G(>)B["]
+ML+6WL*:MO<P1E(^!<'2!M<8_(3[!BN`V.S(-#S`V;8Z&"R<Z:[:VX0<7E>(:
+M`)>^I8(U/1^TO8S6_H:9!3<$M:6P!3\V^X^89DZ'AAD\,O"_ON\Q,!R=ZQ5R
+M@K?I,3@"M+N*!#0%X_`8$(&]M0`[,I2\L/0#'_E0`P_BLK_A/3@$B[&>$F^7
+MY0TQ&K2ZM0\Y-)>+FF=;AX4%/3:<N+_',#%JA)!B7(VUQC,Y&K"[C`(W&N_\
+M'Q6#LHP).C&%O[9_#QCR60`#D+VR<C\Y$+>PDQD5Z,H--Q>QNXPU.`J8M9IB
+M3X6<!C(UAKN];STQ>H&=9M&"B&<R/VJ]N(<(-AF4YQ-AC;"'-#LT@KFW%`@:
+M_%P&!)N\L1`Y/WBQL.L$%N/6##5RL[B$-C@/@;>;:W.9EP,S#X^ZL!D\-MR"
+MGV??@XT1/3+1O[F=-#$=D^P7<(RQDC8[#HJXM1DU&N75!!F&O;0'.#W[LK'X
+M`1+D10T(_;*^EC,X`8^VGA9EDN(-,`:UNK<`/C?CCIEFWX"!&#TPE;F^YC$Q
+M%YGJ:UB/M^8P.0:WN(X!-!O@VAH1@[V(##LPD[RQ6P(<^TX"#)2]O,\\.1FU
+ML9(=8I;R##$<MKJ("#DUD(N98-2&F@<]-YJ[O'8S,6*$EFS<C+5",CX=L[B`
+M##<9Z?<>;HVR@PH[-X>^MV(/'OY(`P"?O+-L/CYKM[&4&&CJW@\W;["[@#0Y
+M"86UF6]>A)(#,@N#NK,6/39+@9-@PXV):#T\9;VXF@HV')?[$GJ/L)HW.PJ/
+MN;03"!G[3`$%A+RV&3@\7["QX`07[5H/-5&RN9DP.0*#M)\52ICJ#3,"B+JV
+M!#\W\(*=8<F"@AP],_F^N90W-A&2X!9,CK&4,3@-M;B(!348YUH''("\M0`[
+M,NZRML(!$.9T#`GCO;_L,CD$CK>2$7^=YP\P!+2ZM0T^-.B.G&'/@(8$/3:3
+MN+_),39KF.EJT(ZW]#,Y!;&XC0`T&>+5!1>-O8\).S&9O+9R`A+E<PT-D+R]
+M3#\^$K6VEAUFD<@.-A&QNX\*.0N<BY]BQ(:8`3TTA[N]8#,V9824;\^/BF4R
+M/Q>RN(8.-QSKPQECC[*!-#LT@+ZT:@\<^G,#!IJ\L!8Y/V:VMNX;;Y52#C=E
+ML[B'-CD,A[6<;M>$D0(R#HRZL!P]-U6!EF+VC(X6/3U&O+F<-3<3EO(2<XZP
+MGS8["8FYBAP('^5Q`!J&O+<$.#WTL+;^!!7L=0XU]+VYDS,Y`8*TDA5%F^\/
+M,P"*NK<!/S3DC9-@\XV#&#PP[[Z^[3<V%)WD%E.)MNTP.`"TN(X'"A_F2P<2
+M@KR*#3LPE[VV4P$6X7P/#NJ\O_\]/AN(MY$1=IS_#C`8M[J+#CXUEXZ28_&#
+MA`8\-IFXO%HP-F*8[17"B;11,CX>L+B#`C0<[5D%:HR]C0L[-H6_MV8"$.1[
+M#`*?O[)G/S\4M+:5'&61W`DV%;"[@C0Y"9F+DFWU@9\#/0J`N[(5,C=VA.MN
+M\(Z*;#T\;;*YA0DT$NK=&62.LH0W.S6-OK46#Q+E>P('A+^Q'3D\=[:VX1MC
+ME4P)-$^RN(4Q.0*&M9-HQ827##(,B;JQ&CPTS(&4;?Z/CQ(\,LR_OI$U-Q:1
+M]1U,B+"0,3@,BKF(&`@=Y'X`&("_M`8X,N>SML@':.]\"0KDO;Z5,CD'C+21
+M%%&;X`XS!K2ZM`,_->V-D6/XC(`%/#&7N;_^-C=HG?@1WHBV^C,Y![>YC`8*
+M'>%P!A&,O(@/.S&2O;=T`13@90X/D;^\TSP^'(NWE!!,G/0(,!RVNHD(/@N0
+MCI!B^8*%`#PWA;N]>#`W9)O@%?&(M'`]/A.SN88--1+O3P5OCKV#-3LTAK^T
+M;P(6YV<,`YN_LV@^/V^TM^P<?Y!="#9BL[N`-SX/FHB0;/*!G0T]"X*ZLQ`R
+M-UB'[FG[B8L4/#Q^O;F9"S01E5$8?XBRFS8["(^^BA,/$.1F#02&O[$;.#W7
+ML;?X&V:4<`@TW;VXGS`^`("UD6C+A^H.,@*+NK8$/#3]@95LY(Z,&3PSY+Z^
+MZS0W%9'&'5*+L)4P.`*UN8X%"!/G9`,?@K^U`SLSZ;.WT0=L[F4("^^\ON,R
+M/@6/M)<7WYKE"#,$M[JU#3\*ZX*78N>,@0<\,9*XO-LV-V*<\Q'(B[;,,CD:
+MMKF#``H3X'D&%X^\C@@[-IF]M'@!:N-A"0R=O[UW/S\0BK3H$%B?V@LQ$;&Z
+MC`H^"9V/EF+D@IH#/#2&N[)M,#1RF^04^8NU9CT_%;.YA`\U$>]V!&.(O8$T
+M.S6`O[45`A?F8P\`A+ZS$3D\9+>WX1]VD$\+-GBRNX<V/@V$B)9O^("0#ST)
+MC[JP'S(TW(?B:.:(B!`\/5F\OI(*-!>51!ATB[.?,3L.B;Z('`\6YV,-!8"^
+MM@0X,O:QM_0:EJVAN;>&X6D$#0LW,CT^/SD\/#`Q-0X&&&Q,XY::AH*,B8B*
+MBK6*BXF.C(*!AYN<D>OA\--,>6%O:A06$1`3$Q,3$!$6%Q1J:6U@9WI\=DE$
+M7=7=VL#,R/7T]_?W]_3URLC.S<#&Q=C?TM'7U51645!24EU<7U]?7UQ<75)3
+M4U!15E975%55U=34U];6UM;6UM;6UM;6UM;6UM;7U]?4U-34U-34U-34U-75
+MU=75U=75U=35U=35U-75U=34U-34U-34U-34U-34U-34U-34U-34U-35U-74
+MU=34U-34U-74U=75U=74U-34U-74U=75U-35U=34U-74U=75U=75U=75U-75
+MU=75U=75U=75U=34U-75U=75U=75U=74U=75U=75U=75U=75U=74U=75U=75
+MU=75U-74U-75U=75U=75U=75U=75U=75U555U=75U=75U=75U=55U=75U=75
+MU=75U=755=75U=75U=35U=75U=75U=75U=74U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=555=755=75U=75U=75U=75U=75U=34U-35U-75U=75
+MU=74U-75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=555555U=55U=75
+MU=7555755=75U=75U=75U=75U=75U=75U=75U=75555555555=755=55U555
+MU575U=75U=75U=75U=75U=75U=75U=75U5555555U=75U=75U=55U=75U=75
+MU=75U=75U=75U=75U=34U=35U=55U=75U=75U=75U=755575U=75U=75U=75
+MU=75U=;FD^[A_L77271Y>&1D9V1D>GA\<W=U341?4=33WL7&PLS)R,C+R\C(
+MSLS"P\;'VMG?W=#6U]545U914%!24E)=75)=4E)34U!045965E=45%555575
+MU-34U-?6U]34U=75U=74U=755575U57555555%54555555555=55U5555555
+M55555=55U575U=555=75U=75U=75555555555=75U=75U=75U5755%155=75
+MU=75U-74U=75U555555555555=755555U5555555U=55U=555555U=555=75
+M5=75U=75U=75U=55U555U575U=75U=755575U575U-5555555=555555U=75
+MU=75U=75U=35U=35U=75U5755=55U=74U-75U-75U=75U=75U-74U=34U=75
+MU=555575U=75U=74U=35U=34U5155=75U=74U-75U=75U=75U=75U=75U=55
+MU5555555U=75U=75U=75U=755=555=75U=75U=75U=75U=75U=74U=34U-34
+MU-35U=55U=55U=?4U=75U5555=555575U=75U-35U-35U=75U=75U=75U-75
+MU=75U-34U-7555755=555=75U=755=7555555=75U=55U=55555555555554
+M5=74U-34U]34U=35U=75U-75U=75U575U5555575U=35U=75U=75U=755=75
+MU=75U=75U=75U=75U=35U=75U=75U=755555U575U=75U=77U]75U=555555
+MU=75U=75U=75U=75U=74U=34U=75U=75U-35U=755=555=555575U=55U=75
+MU=75U-75U-75U=755575U=75U=75U=755535U=75U=75U5755=75U=75U555
+MU=755=755=75U=75U=755=75U5555555U=75U=75U=555=55U=75U=75U=75
+MU=35U-75U-75U-74U-34U]?4U=55555555755=555=75U=75U-35U-75U-75
+MU=75U-75U=34U=75U=75U=75U=74U-35U=55U555U=75U=755=75U=555555
+M5=755515U=75U-34U-34U-74U=755=75U=75U=55555555555555U=75U-75
+MU=555=55U=55U=55U5755575U575U=75U-74U-34U-75U555U=75U=?7U=55
+MU=555=75U=74U-75U-75U-75U555U5555=75U=75U=75U-355555U=75U=74
+MU=75U=74U=75U=75U=75U=34U=75U=755555U5545%55U=75U5555=555575
+MU=75U=75U=75U5755575U575U=75U=755=5555755=75U=34U=7555555555
+M55755=75U=34U-35U=75U=75U=74U]7555555=5555555555U=75U=75U=75
+MU=75U=75U=75U=75U=75U5755=555555U=35U-35U-355555U=75U-75U=75
+MU=75U=5555555%155=74U=34U=75U=75U=55555555755=5555155=555=75
+MU=75U=74U-34U=75U-75U5555554555555155=55U=75U=75U55555555555
+MU-35U=7555755=555=555555555555755=555555U=75U=75U=55U=555=75
+MU=55U=75U=34U-75U=75U575U-75U=75U=75U=755555U5545=35U=35U-34
+MU-34U-75U=55U=75U575U=555=75U=75U=35U=75U=75U=75U=75U=75U=75
+MU=75U=755=75U575U=75U=75U=75U=75U=37U=75U=555=555=75U=75U=75
+MU-75U=75U=35U=75U=75U555U575U=75U=75U-74U-34U-34U=75U=75U=75
+MU-75U=75U=75U=75U=155-75U=75U-75U=75U-75U-75U=555555U=75U=34
+MU-34U-75U=75U=75U-75U=75U=5555555=75U=74U=34U=34U-75U=75U=75
+MU=35U=74U]75U=755=755555U=75U=75U=75U=75U-74U-35U575U=75U575
+M5=755=75U-74U=35U-34U-75U=35U=35U=75U=75U=75U=75U=755575U-37
+MU-34U-34U-35U-75U=755=55555555555=75U=74U-34U-75U=75U=75U=34
+MU=35U=75U=75U=35U=75U=75U=75U-34U=75U=75U]?4U=75U=75U=75U=74
+MU=75U=75U=75U=34U=35U=75U=34U=75U=75U=75U=74U-34U-75U=75U=75
+MU-34U-35U=55U=75U-75U=75U5575%55U-74U-34U-34U=34U=75U555U575
+M5575U=35U=75U=35U-75U=75U=35U=75U555U575U=75U=75U=75U=75U=55
+M5555U=75U=75U=77U=755575U=5555555=75U=35U=75U=75U-34U575U555
+MU5555=75U=35U-75U=75U=75U575U=75U=74U=75U=75U=75U=75U-75U=15
+M5-75U=34U=74U=34U=34U=75U5555555U5555=55U=75U=75U=75U=75U=75
+MU=75U-75U575U=75U=75U=75U=555=75U=75U=34U=75U=74U]35U5555555
+MU=755=75U=75U-75U=74U=75U575U=75U=75U=75U=75U=75U=75U=35U=34
+MU=74U=75U=75U=75U555U=75U-35U=75U535U=74U-34U-34U-35U=75U=55
+MU=55U5755575U=75U=75U=75U575U575U=75U=75U=75U=75U=75U=75U=75
+M5=555575U=75U=55U555U=35555555555555U=75U=75U=75U=75U=74U=34
+MU=75U=75U=75U=755=75U=75U=75U=75U-75U=75U=35U-34U-35U=75U=55
+MU575U=55U=75U-34U-34U-34U-35U=75U=35U=35U=75U=75U=75U=74U-34
+MU-75U=75U=35U=75U=75U=74U-75U=75U=75U=75U=75U-75U-75U=34U575
+M55755=75U=75U=7555555575U=74U-?4U-755=755=75U=355=75U=75U=75
+MU=75U=75U=34U=755=55U=75U=75U=35U-35U=755575U=35U-34U-34U=75
+MU=7555555575U=755555U575U575U=75U=555=75U=75U=75U5555=75U=75
+MU575U=75U=75U=74U=345=755575U-75U=75U5755575U=35U=75U=555=35
+MU=35U=75U=75U575U=75U=75U=55U575U=74U-34U=34U-35U=75U=75U=75
+MU=35U=75U=75U555U=74U-34U-34U-75U=555575U=75U=75U5555=555=35
+MU=35U=55U=55U=75U-74U-75U575U=34U=35U=75U=75U=74U=75U5555555
+MU=?4U=75U575U=55U575U=755=75U=75U=75U=75U575U575U=75U-755=75
+MU=75U=75U=75U=34U=74U=75U=35U=35U=75U=75U=75U=54U=34U-34U=34
+MU-74U=75U=75U=75U=75U=75U=75U=755=75U=75U5755=55U=75U=75U-75
+MU=75U=75U=75U=7555555=75U=75U-74U=77U]74U=755=55555555555=75
+MU=75U=75U=75U-75U=75U=34U-34U=75U=75U=75U=74U=35U=75U555U=75
+MU=74U=75U5755=755=755%34U-77U-34U-75U=75U=75U=755=555=555=75
+MU=75U=34U=34U]35U-75U-75U-35U=75U=75U-74U=75U=75U=75U-74U-34
+MU-75U575U-35555555755=755=75U=75U-34U-35U-75U-75U=75U=75U=75
+MU575U=75U=74U=75U=75U-74U-75U=74U=75U=75U=75U=35U=74U555U-75
+MU-35U=35U=75U=75U=555=75U=75U=75U=75U=75U-75U=7555555=55U=75
+MU=75U5555=755=75U=75U=35U=75U=755=75U=75U=?7U=75U5555555U575
+MU=75U-35U=75U=75U=755575U=75U=75U-75U=75U=75U=75U=75U=75U=75
+MU=34U-34U-34U=75U=75U=75U=545=75U=35U=75U=75U=755=55U=75U575
+MU555U575U=75U=75U=75U5555=75U=75U=75U=75U=75U=75U=75U=555=55
+MU=35U=75U=34U=37U]7555755575U555U=755=555=55U=75U=34U-75U=75
+MU=75U=35U=75U=75U=55U5555=75U=75U-35U-35U-75U=75U=75U-35U=75
+M5535U=75U=34U-34U-74U575U575555555555=555=75U=35U=34U-35U=75
+MU-35U=75U=555=75U=75U=55U555U=55U=74U=74U=74U=74U]35U-75U=75
+MU=75U=75U=75U=74U=35U-34U]34U=7555755575U=75U=75U=34U=34U-74
+MU=74U=74U=75U=75U-34U=75U=755=75U555U=74U-34U-74U-34U=75U=75
+MU=75U=555=55U=75U=75U=75U=355555U5555575U=75U=75U=55U=75U=75
+MU=75U=74U=75U=75U=55U=34U=555=755=55U=55U=75U-75U-35U=75U-75
+MU=5555555575U=555=75U=75U=75U=75U=75U=75U-75U=75U=35U-34U-75
+MU=74U=555%75U=34U-34U-34U=75U=755=75U=75U=75U=74U-34U-75U=74
+MU-55U=75U=75U=755=75U=75U=34U=75U=75U=55U=75U=75U=75U=77U]34
+MU=7555755=75U=75U575U575U=55U=75U=75U=74U=75U=355555U=75U=75
+MU=75U=75U=75U=74U-34U-35U=75U=75U=75U=755574U-34U-34U-35U=75
+MU=555=7555555555U555U575U=75U=75U-55U=75U=75U=555=75U=75U-75
+MU=75U-75U=75U-34U-75U=74U=75U-?4U=74U=74U-74U575U5755=75U=35
+MU-34U=75U=555555U575U%75U=75U=75U=75U=74U-34U-34U-75U=75U=75
+MU=74U=74U=75U554U=75U-34U-35U-35U=75U=75U=75U=34U-75U=75U=35
+MU=34U=35U=75U=35U-34U=75U=75U=75U575U=55U=75U=75U=75U=75U=35
+MU=37U=75555555555=75U=75U=75U=75U=75U=35U=75U5555555U=55U=75
+MU=75U=75U=75U-74U-34U=74U=7555755=75U=75U-75U=555%55U=75U=75
+MU=74U=75U555U555U555U=55U=75U=55U=75U=74U575U=75U575U5555575
+MU=75U-34U=74U-35U=35U=35U=75U=34U=74U-75U=555=755=55U=75U575
+MU=75U575U=74U-35U=75U=75U=34U=75U=75U=755=755=75U=35U=75U=75
+MU=75U=75U-35U-75U=345535U=74U-34U-74U-34U-74U-75U=75U=75U=75
+MU=34U=75U=75U-555=74U=75U=75U=75U=75U=35U=35U=74U=75U=555555
+MU575U=75U-?4U5755575U=74U=755=75U=75U=75U-74U-35U=75U=75U=75
+MU575U=55U=75U=75U=75U=34U-34U]34U-75U=755=55U=75U575U5545=75
+MU-74U-34U-35U=75U5755=755=7555555575U=755=75U=75U=75U=755=55
+MU=75U=755575U=75U=75U=75U=74U-74U-75U=755=37U=75U=555=755=75
+MU=75U=755=75U=75U=75U=75U=75U=75U-75U575U=75U=75U-75U-75U=75
+MU=75U=75U=74U=74U=74U=75U=355-74U-34U-34U-75U=75U=75U=34U=75
+MU575U=75U-74U-74U-74U=755=75U=755%74U-?5U-=55=555-555=75U-55
+MU-34U=75U-35U=74UM75U5555555U575U=75U=75U=75U-34U-74U-74U535
+MU-755=7455555=75U=75U555U555U=75U=77U-35U=34U=75U=75U=55U=55
+M5515U=74U-?4U-35U-75U55555755=74U=75U555U=75U=75U=74U-34U-34
+MU=75U555U555U=75U=75U=75U=75U=75U=75U575U-74U-55U=34U555U=55
+M5555U=75U=75U=75U=75U=74U=75557555555=555555U=55U=75U=35U=34
+MU-35U-35U=75U=55U=75U-35U=55U57555545=75U]34U-34U=75U=75U=75
+MU=55U=7555555555U=74U=75U=34U=555=55U=75U=55U=75U=75U=75U=75
+MU=34U-34U-75U-55U=55U=77U-75U=55557555755575U=75U=74U-74U-35
+MU=75U=75U=75U=75U=755575U=75U=75U-34U=75U-75U=75U=75U=74U-34
+MU=35U=755%35U=34U-34U-34U=75U5555555U=75U=55U5755=75U=75U=75
+M5575U=75U=75U=75U=75U=755=75U5755575U575U=75U=75U=75U=74U]35
+MU=75U=75U=555=75U=55U=75U=55U=75U=75U=75U=75U=75U575U575U=75
+MU=75U=75U=75U=75U=75U=75U575U=75U=74U=34U%15U=75U=34U-34U-75
+MU=75U=75U=555575U=75U-34U-75U-75U-75U=75U=75U=755=755=75U=75
+MU=75U-75U=75U5555=75U=75U=35U-?4U=75U575U=75U=75U=755=555555
+MU=75U=75U=75U=75U=75U=755=75U=555555U=75U-34U-34U=75U=75U=75
+MU=75U=75U=55U=545-55U=35U-35U=34U-34U=75U=75U575U=75U=75U-34
+MU-75U=35U=755=75U=75U=75U=75U=75U=75U=35U=75U=75U=74U-75U=75
+MU=37U-75U=5555555555U=75U=75U=75U=75U=35U=755=75U=75U=75U=55
+MU=75U=75U-75U=75U=75U-75U575U=75U575U=75U-35U-345=75U=34U-34
+MU-74U=75U=75U=755555U=555555555555555575U=75U=75U-75U=75U=55
+M5575U-35U=75U-75U=75U=74U=34U-34U-75U]?4U555555555545=75U=75
+MU=75U=75U=74U=74U=75U=555555U=55U=55U=75U=75U=34U=75U=75U=75
+MU=75U=74U-75U=35U=75U575U-34U-35U-35U-34U-35U-34U=75U=75U=75
+MU-75U-74U-75U-75U=555575U=75U=34U-74U=355=755575U=74U-75U=75
+MU=55U=55U=?4U=75U57555555=555575U=35U=75U-75U=75U575U=74U-34
+MU=75U=75U575U=75U=75U-75U-75U=75U=75U=35U-35U=34U=75U=555=75
+MU=34U]34U=75U=755=75U=34U575U=75U=75U=75U=74U-74U=35U=35U=75
+M5=755=555555U=75U=75U=75U=75U=55U=75U=75U=77U]34U55555755555
+M5=55U=75U=75U575U=74U=75U=75U-75U575U=75U=75U575U=34U-35U-35
+MU=35U=34U-34U=74U=75U=75U-755574U-34U=34U-75U=75U=74U=74U=75
+MU=75U=75U=75U=75U=34U=75U=35U=75U=34U=75U5755=75U555U5555575
+MU-35U=75U=34U575U-?4U55555555555U=75U=75U=75U=35U=755=75U=75
+M5=75U=555=55U=55U=55U=74U-75U=75U-75U=75U=75U=75U-75U=75U=75
+MU515U=34U-35U=35U=75U-74U-35U=75U-75U=75U=75U=755=75U=75U=74
+MU=74U-75U575U=75U=75U=75U=755575U=75U=75U=75U=55U=34U=75U=55
+M5=55U=75U=75U=75U=755=55U=75U-55U=75U=55U575U=75U=75U-35U=74
+MU=75U575U=75U=75U=75U=75U-75U=34U=755-745=35U=35U=75U=75U-15
+MU=55U=7555555555U575U=755575U=75U555U=555=55U=75U=75U=75U=75
+MU=34U-75U=75U=755=74U-34U]35U-155555U555U575U=75U=75U=555=75
+MU=75U=74U=75U=555=74U=755=74U=75U=34U-75U=35U=55U=74U=75U=35
+MU=555=755515U=74U-75U=75U=75U=75U=75U575U=5555555575U=75U-35
+M5=75U=75U=75U=35U=75U=75U=555575U=75U=75U-35U-34U=75U555U=35
+M55755=5555555575U=75U=75U=75U=75U=34U=35U=55U=75U=755=555555
+MU=75U-75U=75U=75U=74U-35U=74U=75U-34U%75U554U=74U-34U=35U=75
+MU=75U=75U=555575U555U=75U-34U-75U-75U=75U=75U-75U=75U=75U=55
+MU555U=75U=75U=75U=75U=75U=35U=37U55555555555U=75U-74U=35U=75
+MU-35U=74U-34U=75U=75U=75U=75U=75U=75U=75U=74U-35U=74U-75U=75
+MU=75U=75U=35U=355=75U=34U-34U=75U=5555555575U=55U=34U-75U=75
+MU=5555555=75U=75U5755555U=74U-34U=75U=35U=75U=75U=74U-75U555
+M5574U%555=755=75U=555=55U=75U=75U=34U=34U-75U575U=75U=75U=75
+MU=75U=75U=75U=34U=75U=75U=75U-35U=75U=75U=75U=355575U=34U-34
+MU=34U575U=75U-75U=75U=5455545=75U=74U=34U-34U=75U=75U=75U=75
+M5=555=555575U=75U=74U-34U-34U=75U=75U=355=75U=75U=55U=755=75
+MU=75U=75U=75U=755=75U=75U=55U=75U=55U=755=74U-34U-35U-75U=55
+MU=34U-75U=755575U-74U-55U=75U-34U=34U-75U=35U=75U=55U=75U515
+M5=755=77U=74U=75U-35U-15U=75U555U=75U=34U%75U575U-75U-34U=75
+MU=555=555=77U-75U=75U=75555555555=75U=34U-75U-34U=55U=55U=75
+MU=75U=35U=75U555U=75U-75U-34U=755=75U=75U=34U-34U-55U=755%15
+MU=34U=34U=34U=75U=75U=75U=55U=75U=74U-75U=75U575U5755555U=55
+MU=75U=75U-74U-37U-75U=75U=75U=75U=75U=75U=74U]35U=75U=755575
+M5=75U=74U=75U=55U=75U-35U=75U5555=75U575U=75U-75U=34U=55U=75
+MU=74U=34U=75U=75U=35U=15U=75U515U=34U-35U-75U=34U-35U=555=75
+M5=55U5555575U=75U=75U=555=555=75U-35U=755555U=75U=35U=755575
+MU=555=74U-75U-35U=34U=5555155=555=75U-75U-34U-74U=34U=75U=75
+MU=75U=755=7555555=75U-34U-35U5555=75U=74U=34U-75U=75U=75U=75
+MU5545=75U-75U=35U-34U-74U=5555555575U=35U=75U=75U555U=74U-74
+MU=75U=75U-34U=555=75U=75U=75U=75U=75U=75U=55U=75U=77U]75U554
+M55155=75U=34U-35U-755575U=34U-34U=75U=74U-34U-34U=75U=75U=75
+MU=74U-75U-75U=75U=75U=34U=35U-3555755%35U=37U-34U-35U=755=75
+MU=75U=75555555555=55U=75U=75U=75U-55U-34U-75U=555575U=755555
+MU=34U-75U=74U=75U=75U575U-35U5755575U575U=74U=75U=75U=75U=75
+MU=75U=75U=75U-35U-75U=75U=75U=75U575U=75U=75U=75U-35U=75U=75
+MU=74U=35U554U=34U-34U-35U=35U=75U=75U=75U=75U=75U=34U-75U=34
+MU=55U=55U575U=74U-75U=75U=75U=74U575U=75U=75U=75U=75U555U=?4
+MU=75U55555545575U=74U=35U=75U-35U-34U-75U-75U=75U=75U=755575
+MU575U=74U-75U=75U=75U=34U-35U=35U=75U575U-545=35U-75U=35U-35
+MU=34U-35U-75U-75U=75U575U=74U=75U=75U5755=74U=74U-34U=55U555
+MU575U=75U=35U=34U=75U=55U=75U=74U%75U5545575U=75U=75U-74U=34
+MU-75U=75U=74U-34U=75U5555575U=75U=755=55557555555575U=75U=75
+MU=55U575U555U5755-74U-34U-34U=75U=75U=35U-35U=75U=55U575U=75
+MU=75U=74U=75U=75U=75U=75U=75U=75U=75U=75U=755575U=75U=74U575
+MU=75U-15U5555575U=75U=75U=75U=74U-75U=75U=55U=75U-75U=75U555
+M5555U=75U=75U=34U-75U=74U=34U-75U575U=75U=35U=35U515U=74U-34
+MU=74U=75U=34U=35U-755575U=75U=74U-35U=35U=75U-74U=75U-34U=75
+MU=555=75U5755=75U=35U=75U=75U=75U=55U=?7U=55555555555=75U=75
+MU=555575U=75U-74U=75U=55U=75U=755=75U=34U=35U=35U-75U=75U=75
+MU-35U=75U=555575U=75U=555=75U-35U-34U-75U=75U=75U=75U5555575
+MU575U=74U575U-34U-75U=755=75U=75U=75U=755=75U=75U=55U=75U=55
+MU=55U=75U=77U]75U=75557555555555U=75U=75U=75U-37U-75U=75U=75
+MU57555555555U=75U-74U-75U=75U=75U=35U-75U=75U=75U=75U=755574
+MU=34U=74U=75U=75U=74U=34U=75U=34U=75U-75U=75U-755=75U=74U-34
+MU=75U5755=555555U=75U=75U-35U-74U-75U=75U=75U-35U5555%15U=75
+MU=74U=34U-75U=75U=74U-35U=74U=75U=15U=74U-75U-75U=75U=34U-55
+MU575U=75U=75U575U=74U=755=555514U=74U-75U=75U=75U=75U=75U=74
+MU=34U=75U=75U-74U-34U-75U=75U-75U-75U555U555555555755=75U=34
+MU=34U=35U=75U=75U=34U=555555U5755555U=35U=75U-35U=75U=75U555
+MU555U575U=75U=75U=75U-34U-34U=75U=75U=755575U=74U-74U-34U=75
+MU=755-75U-34U-34U-34U=35U=55U=55U=55U%55U=75U575U575U=75U575
+M5=755=75U=75U=35U=75U=35U-34U-34U=75U=34U=35U=75U=74U]75U=55
+MU5555555U=75U=75U=75U575U=74U=755575U=75U-75U=75U=35U-75U-75
+MU-74U=35U=75U=75U=75U-35U=75U-75U=755535U=34U=34U=75U=75U=75
+MU=75U=75U575U=755=75U=75U-3555555555U=75U=35U=35U=75U-75U=75
+MU=75U-35U-34U-74U=75U-75U-34U=75U575U55555755575U=34U-74U-75
+MU-75U=34U-34U-35U=75U=75U=75U-35U-35U=75U=755=75U=75U=75U=75
+MU5755=75U555U=74U-74U-35U-34U-35U-35U555U5555555U5755=75U=34
+MU=75U=35U=35U-34U-35U=75U575U=75U=75U=75U-75U=75U=75U=74U=36
+MU-75555455555=75U=75U=75U-75U-74U-75U575U=555=755555U=75U555
+MU=75U=75U=35U=75U=75U=75U=75U=35U=75U-75U=745%75U=34U-34U=34
+MU=34U-75U=55555555555575U=75U=75U-75U=75U-75U-74U-75U=75U=75
+MU=75U=75U=75U=75U=755=755=75U=74U]35U=5555755575U=75U=75U=75
+MU=75U=75U=75U=75U=74U55555555=55U=75U-34U=75U=75U=75U=75U=75
+MU=75U=35U=75U=75U515U-74U=75U-34U=75U=34U=75U555U5555555U=75
+MU=75U=7555555=75U=74U=74U=755555U=75U=75U=75U=75U=75U=75U575
+MU=75U-?4U=75U555U=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U-34U-35U-75U-74U-74U=75U=545-75U=34
+MU-35U=75U=75U=75U=75U=75U=75U=75U=75U=75U5555555U575U=555555
+M55555=75U=75U=75U=75U=75U=75U=75U=75U=37U-5555555=555=75U-35
+MU-34U-35U=35U-34U-34U=75U=35U=35U-75U-74U-755=75U=75U=75U=75
+MU5555=755=75U=755=75U=355=74U=35U=75U-34U-75U=75U=75U=75U=75
+MU-75U=75U=75U-35U=75U=75U-75U=755555U=75U=75U=75U=75U=75U=75
+MU=35U-35U=75U-35U5555555U575U=75U=75U575U=75U=75U=75U=75U=74
+MU575U=755=75U=74U=34U=34U=75U=75U=75U=75U=75U=75U-35U=74U515
+MU=74U-34U-74U=75U=75U=75U=75U=7555555575U=75U=355=75U=75U=75
+MU-34U-74U=35U=75U=75U=55U=75U555U=75U575U=75U-?4U=75U575U=55
+MU=75U=75U5755=75U=74U=75U=75U=75U=5555555555U=75U=75U=35U-34
+MU-75U-75U=75U=75U=75U=75U575U=545-75U=34U-34U=35U=75U=75U=75
+MU=75555555555=75U=74U=5555755=555=55U=75U575U575U=75U555U555
+MU=75U=74U=75U=75U=77U-7555755=55U=755=55U=75U=34U-34U-35U=55
+M5=55U=35U=75U=75U=75U=75U=75U-75U-75U=75U=75U=75U-35U=74U-75
+MU=555%75U=34U-34U-34U=75U=55U=75U=55U=55U=75U=55U=75U-75U=75
+MU=75U=55U=755555U5755=755=75U=34U-34U-34U-34U-34U-74U]?4U-75
+M5555U=75U=35U-75U=75U=55U=74U-34U-75U=75U575U=75U=75U=74U=34
+MU-74U=755=755=75U-34U-35U=55U=75U=75U575U-34U-34U-75U=35U=75
+MU=75U=75U=7555545555U=74U-?4U=75U=75U=74U=34U=34U-35U=75U=75
+MU=35U=34U-75U=75U575U=55U=34U=55U55555555555U=75U575U=75U=74
+MU=75U=75U=75U=555575U=75U=755=555=75U=75U=35U-75U=75U=75U=75
+MU=75U-35U=55U=34U-35U-75U=75U=75U575U=75U=75U=75U=75U=34U-34
+MU=75U=75U=75U=75U=75U=55U=55U=75U=75U=35U=555=75U=75U=75U=37
+MU]75U=555=75557555755=75U=75U=74U-34U=555=75U=755=55U=75U=75
+M5=75U=74U-35U-75U-75U=75U-75U-75U=75U=74U=755%74U=34U-74U=75
+MU=75U=755=75U=75U=75U=5555555=75U=55U=75U=75U=555555U=75U=55
+MU=555575U57555755=75U57555755=75U-35U=55557555555555U575U=75
+MU=35U=75U=75U=755=75U=75U55555555=75U=74U-34U-35U=75U=75U=75
+MU=75U=755=755575U555U=34U-35U-35U=755=755=75U=75555555555=75
+MU=75U-34U=75U=74U=75U-34U-74U=74U-75U=34U=35U=75U=75U=75U=55
+MU=55U=?4U=55U5555555U=55U575U=75U=75U=75U=75U=75U=75U=75U=75
+M5=555=755=75U=75U=74U=75U=75U=75U=75U=35U=75U=75U-145=75U-34
+MU-74U-35U=755575U=75U=74U=75U=75U=34U=74U=74U=75U=75U=75U=75
+MU=75U=75U=55U555U=55U=75U=74U=35U-34U=77U]7555555555U=755=75
+MU=75U=74U=34U=34U=75U=75U=355=75U=75U=75U=75U575U=75U=34U-75
+MU=75U=75U=75U=75U-75U=345574U=34U=34U-34U=34U-75U=75U=75U=75
+MU=75U=34U-35U-355=75U=75U-35U-35U-75U=55U=75U575U575U575U=75
+MU=74U=74U=74U-?5U=55551555555=75U=75U=75U=75U-74U-75U=75U=75
+M55155555U=74U-74U=34U-35U=75U=755=75U=75U=55U=75U=75U=75U557
+M5=75U=74U-75U=75U575U=75U=75U5555555U=75U-74U-34U=75U=74U-75
+MU=75U=74U=35U=35U=75U=74U-74U-35U=75U=75U5555=34U=555=555=55
+MU=75U=74U=34U-74U-75U=75U=75U-74U-35U=75U=75U=75U=75U=75U=75
+MU=35U=75U=755=75U=35U-34U-35U=555-75U-34U-34U-35U=75U=555575
+MU5755=755555U5555=75U575U=75U=75U=7555555=55U=75U=75U=55U=55
+MU=75U575U=55U=75U=74U]75U=75U=74U=75U=75U5755575U=75U-34U=75
+M5555U=755=75U=755575U=75U=75U=55U=75U=75U=75U=75U=75U=75U=75
+MU-355575U=34U-34U-75U=34U-74U=75U=75U=75U=75U=75U=74U-35U=75
+MU=75U=75U=75U-74U-75U=755575U=75U=34U-35U=75U=75U=75U-?4U=75
+MU5755%555555U=75U-34U-35U=75U=75U=75U=75U=75U=75U=75U575U=74
+MU=34U-34U-75U=75U=74U=75U-75U=755555U=545=75U-34U-34U=75U=55
+MU=55U=75U=75U=555555U=75U=74U=75U=75U575U=74U=75U=75U=755=75
+M5=755=75U=34U-75U=75U=755=37U=75555555555555U=74U-34U-34U-75
+MU-34U-75U=75U=55U575U=75U=75U=74U=34U-75U=75U=75U=75U=75U=55
+M5=75U=35U-355=75U-35U-35U-35U-75U=75U=75U575U=75U=74U=75U575
+MU5755=74U=34U=75U=75U=75U=75U=34U-75U=75U5555=75U=74U=74U-74
+MU]355=75U=75U=75U=75U=75U=75U575U=74U-75U=75U=555=55U=35U=75
+MU=75U=75U-74U=75U=75U=34U=75U=75U=75U=74U-34U535U=74U=74U-34
+MU=75U=75U=75U=75U=7555555=755575U=75U5755575U=75U=75U=35U=75
+MU=55U555U575U=75U=75U-75U=75U-35U-?4U=755575U=755=755=55U=75
+MU=75U=35U-35U=75U=35U=75U=75U=555575U=75U=75U=75U=75U=35U=75
+MU=75U=34U=74U-75U5545-75U-34U-34U-34U-75U=7555555=755555U=75
+MU=75U=75U=75U=75U-74U-74U=75U5555=75U=75U-74U=34U=75U=75U=75
+MU=35U=37U=5555555=555=55U=75U=75U=75U=75U-74U-75U=75U5555555
+M5=75U575U=74U=34U-34U-75U=35U=35U=755=75U=75U=75U=755-75U-35
+MU-34U-75U=75U=75U=75U=75U-75U=75U-34U-75U=75U=75U=75U=75U=75
+MU=755=75U=75U=75U-35U=75U=75U=75U=75U=74U]35U5555575U555U=75
+MU=75U=75U=75U=75U575U=75U=75U=75U=75U=55U=75U=75U-35U=55U=75
+MU=75U=755575U=75U=75U=75U535U-74U-75U-74U=75U55555755=55U=75
+MU=755=75U555U=755575U=75U=75U=75U=55U=75U=75U=75U=75U575U=75
+MU-35U=74U-35U-?4U=55U5755555U=755=75U=75U=74U-34U-34U-35U575
+MU=75U=75U=75U=75U=75U=75U575U=75U=75U=75U=74U-35U=75U=75U=15
+M5=75U=34U-74U-35U=75U=75U=75U575U=55U575U=755=75U=75U=75U=75
+MU=75U=75U5555=555=75U=75U=75U=75U=75U=75U=555=74U-5555545555
+MU=75U=75U=74U=75U=75U=75U=75U=75U555U=75U=75U=75U=75U=75U=75
+MU=75U=74U=34U=75U=75U=34U=75U=555%75U=34U-74U-75U=75U=755=75
+M5555U=555575U=75U=75U-74U-34U-34U=74U575U=75U=75U=75U=75U=75
+MU=75U=35U=75U=75U575U-155=555=75U=75U=75U=75U-75U-35U=34U-35
+MU=7555755=75U=75U=755=75U=75U=75U=74U-34U-34U-75U=75U=35U-34
+MU=75U515U-75U-74U-74U-74U=75U=755575U=755=755=75U=75U=75U=75
+MU=75U=55U=75U=55U575U=75U=75U=75U575U=75U=75U=75U=75U-?7U=75
+MU575U5755575U=75U=75U=755=75U=75U=75U=75U=55U=75U=75U=75U=75
+MU=75U=75U-75U=75U=75U=75U=75U=75U=75U=555=75U-75U-34U-34U=75
+MU=75U=75U55555555555U=75U-34U-35U=75U=35U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=74U-7555555=75U=75U=75U=75U=75U=75
+MU=34U=555=75U=55U575U=75U=75U=74U=75U=35U-74U-75U=75U=755575
+M5=75U=75U=345535U=34U=34U-74U-34U=34U=75U=35U=75U=75U=75U=75
+MU=75U=74U=75U-74U=35U=75U575U575U=75U=75U=74U=75U=75U=74U=75
+MU-35U55555555=75U=75U=75U-35U=75U=75U-75U575U=75U=75U=75U=75
+MU575U=74U=75U=75U=75U=75U=75U=75U=75U=75U=75U555U-74U-34U-35
+MU=75U=55U555U=75U=75U575U=755575U=75U=75U=75U575U=755=755=75
+MU=75U=75U=75U=75U=75U=35U=75U=755=34U=75U575U=75U=7555755=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=34U-35U=55U555
+MU=75U=75U=34U=75U=155=75U=34U-34U=75U=75U=75U=75U=74U=35U=35
+MU=35U=75U=75U=35U-35U-34U=755=555=75U=75U=75U=75U=55U=75U=34
+MU-34U-37U]55555555555=75U=74U=75U=75U=75U-34U-75U=75U=55U=75
+MU=75U=75U=75U=75U=74U=75U=75U=74U-34U=755=75U=75U=74U535U=74
+MU=74U-34U-75U=75U=7555555555U555U=75U=75U5555=75U=75U-74U=75
+MU5755575U=7444%64TQ37-34TM_>V-C8V=G?W]+2T]S>45C81%#&6]907515
+M1E344=9`T-13UE-7WUE6TE_?T%/25]#04]?65MW75=95UM965-15U]76T]54
+MUU96TE35UU'6U5=45=745%#7U%165M?5U=?5U-!6T]57TU/7UU+25%?15M'6
+M4M?7U=96U]74U=54UM75U5;45]?7U==7U57545365-=6U=17U5775=;45=55
+MU%=5U5365%755M95U-16UE755%575==1UN2AHZ4J*BXA.30%6Y*#B[>QL+.S
+ML["VM[6(C(&%G97DW'-B%!`<&!H%!`0$!`0%&A@>'1,6%&EB9W]T1%39S/?R
+M_OOEY.?GY^3DY?KY__+P]\H)(*NFL)P=-3T[)"8F(28F)R4[/S(V"P,;%4+G
+MD9N&@H^(B[6UM;6*BXB.C(*!AYJ>DY3LY/?307-E86UN:VH5%106$1`0$184
+M%6EM87IR2%O4V\_W\O[[Y>3DY^?DY.7[^?SR\?3(PL39T]=74U];149`0TU-
+M34Q-34)#0$%'1%I;6%Y?75-045975%75U=34U-34U-35U=5555555%145U=7
+M5E965E965E9645965E9645965E975E975U=75U=75%145%145%145%145%14
+M5%145%145%145%145%145%=45%175%145%155%1455155%55555555555555
+M5%555555555455555%555555555555555555555555555555555555555555
+M55555%5555555555555555555555555555755=55U=75U=75U=75U=755=75
+M55755=555=5555555=75U=75U=75U=55U=75U=75U=75U=75U=75U=75U575
+MU=75U=75U=75U=75U=75U=75U=55U55555755575U555U=75U=75U=55U575
+MU=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=74U-75U-35U-34U-75U-75U=75U=75U5755=555=555555
+M5555555555555575U555U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=55U=75U=75U=75U=75U=75U=75U=75U=75U=75U=55U=75U575U=755=75
+MU=755=75U5755=75U=755=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U-75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U-75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=31T=?45=755%165E=45%15
+M5=75U=74U-74U-34U-34U=34U-75U=55U=74U-745%74U%;5E[2U@(25Y'9@
+M%A(9&P4%!1H8&1P2%A5O87YU4M[U_>7FX^WO[NGIZ>[O[.WCX>?E^?WVRL+:
+MW==67%M'0TU.24A+2TA(3D],0D!&1%M97UU345=5U=36UM'0T]/3T]/3T-#0
+MT=;6U]?7U-7555545%145U=75E=75E=75U=75U=75U175%145%1555555555
+M55555555555555555555555555555555555555555555555555555555U555
+M5=555=55555555555555U5555555557555755555557555555555U575U=75
+M5=75U=75U=75U=75U=75U=75U=55U=75U575U555U5755=55U555U575U=55
+MU=75U575U=75U=755555U555U575U=75U=75U=55U5755=555=755=55U=75
+MU=55U=75U575U575U=755=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U555U555U555U575U=55U575U=75U575U=75U=755=75
+M5=55U=55U575U=75U=75U=75U=75U=75U=35U-75U-75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=74U=35U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=35U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U575U=75U=755=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=755=755=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=55U=75U5755=55U=55U=75U=75U575
+MU=75U=55U=55U575U=75U=55U=75U=55U=75U=75U=75U=75U=75U=75U=75
+M5=75U=75U=75U=75U575U5755=75U=75U=75U=75U=75U=75U575U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=35U=75U=75U=55U=55U=755=75U=75U=75U=75U=55U=75U=75U=75U=75
+MU=75U=75U=75U=55U575U57555755=55U555U5755=55U555U555U575U575
+M557555755=555=555=555=55U57555755555U5755=555=5555555=555575
+M5555U555555555555=555=55U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=755=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=755=755=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=55U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U575U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75U=75
+MU=75U=75U=75U=75U575U=75U575U=75U=755=75U=75U575U=75U575U=75
+MU=75U=75U=75U=75U=75U=75U=75U=7_____________________________
+M__________________________________________________________]5
+M555555555555555555555555555555555555555555555555555555555555
+M55555555555555555555555553HX,@L1DHJ]N+JXO8J2$0LR.#HX/34<E(BR
+MN+J[O+29:PDS.3H[/#0:[8ZSN;J[O+>%9@PP.3H[/S<'_HVPN;J[O[:&=`(Q
+M/CH[/C8!V8.QOKJZOK&#W`$V/CLZ/C$"2H:VO[NZN;"-_P<W/SLZ.3`,9X2W
+MO+NZN;./[1HT/#LZ.3,):)FTO+NZN+*(E!\U/3@Z.#(+$9*UO;BZN+V*DQ$+
+M,C@Z.#TU')>(LKBZN[RTF6L),SDZ.SPT&^R.L[FZN[RWA6$/,SDZ.S\W!/B-
+ML+FZN[^VAG<",#XZ.S\V`=J#L;ZZNKZQ@-,`,3X[.CXQ`DF&MK^[NKZPC?T'
+M-S\[.CDP#&2$M[^[NKFSC^(:-#P[.CDS"6F8M+R[NKBRB)0?-3TX.C@R"Q:2
+MM;VXNKB]BI,0"C(X.C@]-1R7B+*XNKN\M9YJ"3(X.CL\-!OOCK.YNKN\MX5A
+M#S,Y.CL_-P3ZC+"YNKN_MH9Q`C`^.CL_-@''@[&^NKJ^L8#6`#$^.CH^,0-,
+MAK:_N[J^L(WR!S8_.SHY,`QDA+>_N[JYLX_C!30\.SHY,PYNF+2\N[JXLHB5
+M'S4].#HX,@L6G;6]N+JXO8J0$`H].#HX/34=EXNRN+J[O;6>%0DR.#H[/#08
+M[HZSN;J[O+>%8`\S.3H[/S<$Y8RPN;J[O[:'<`(P/CH[/S8!P(.QOKJZOK&`
+MU0`Q/CHZ/C$#0(&VO[NZOK""\`<V/SLZ.3`,982WO[NZN;./X`4W/#LZ.3,.
+M;IBTO+NZN+*)E1XU/3LZ.#(+%YVUO;BZN+V*D!`*/3@Z.#TU'9:+LKBZN+VU
+MGQ4(,C@Z.SPT&.Z.L[FZN[RTA6,/,SDZ.S\W!.2,L+FZN[^VAW(-,#XZ.S\V
+M!LR#L;ZZNKZQ@%8`,3XZ.CXQ`T>!MK^[NKZP@O$&-C\[.CDP#'J$M[^[NKFS
+MC^$%-SP[.CDS#F^;M+R[NKBRB>H>-3P[.C@R"!><M;VXNKBRBI`3"CTX.C@]
+M-1*6B[*XNKB]M9\4"#(X.CL\-!CIB;.YNKN\M)IB#S,Y.CL_-P3DC+"YNKN_
+MMX=]#3`^.CL_-@;)@K&^NKN^L8!3`#$^.CH^,0-:@;:_N[J^L(+W!C8_.SHY
+M,`UXA+>_N[JYLX_A!3<\.SHY,PYLF[2\N[JYLXGK&30\.SHX,@@7G+6]N+JX
+MLHN1$PH].#HX/342D8NRN+JXO;6?%`@R.#H[/#09Z(FSN;J[O+2:;0\S.3H[
+M/#<$YXRPN;J[O[>'?PTP.3H[/S8&RH*QOKJ[OK:!7`,Q/CHZ/C$#68&VOKNZ
+MOK&"]`8V/SLZ.3`-?H>WO[NZN;",Y@4W/#LZ.3,.;9JTO+NZN;.)ZQDT/#LZ
+M.#((%)RUO;BZN+*+D1(*/3@Z.#T*$I&+LKBZN+VUG!0(,C@Z.SPT&>N)L[FZ
+MN[RTFFT.,SDZ.SPW!>:,L+FZN[^WAWX-,#DZ.S\V!O2"L;ZZN[ZV@5D#,3XZ
+M.CXQ`UR!MKZ[NKZQ@LH&-C\[.CDP#7^'M[^[NKFPC.<$-SP[.CDS#VV:M+R[
+MNKFSB>@9-#P[.C@R"!2?M;VXNKBRBY$2-3TX.C@]"A.1B[*XNKB]M9P7"#(X
+M.CL\-!GKB;.YNKN\M)ML#C,Y.CL\-P7AC[.YNKN_MX1X#3`Y.CL_-@;W@K"^
+MNKN_MH%:`S$^.CH^,0!3@+&^N[J^L8+)!C8_.SH^,`U]A[>_N[JYL(SD!#<_
+M.SHY,P]BFK2\N[JYLXGI&#0\.SHX,@@4G[6]N+JXLHN6$C4].#HX/0H3D(JR
+MN+JXO;6<%P@R.#H[/#4>ZHFRN+J[O+2;;PXS.3H[/#<%X8^SN;J[O[>$>@PP
+M.3H[/S8&\8*POKJ[NU555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+M555555555555555555555555555555555555555555555555555555555555
+755555555555555555555555555555555
+`
+end
diff --git a/usr.sbin/i4b/g711conv/Makefile b/usr.sbin/i4b/g711conv/Makefile
new file mode 100644
index 0000000..f24d6e8
--- /dev/null
+++ b/usr.sbin/i4b/g711conv/Makefile
@@ -0,0 +1,11 @@
+#---------------------------------------------------------------------------
+#
+# $FreeBSD$
+#
+# last edit-date: [Thu May 20 11:58:43 1999]
+#
+#---------------------------------------------------------------------------
+
+PROG= g711conv
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/g711conv/g711conv.1 b/usr.sbin/i4b/g711conv/g711conv.1
new file mode 100644
index 0000000..ca3845b
--- /dev/null
+++ b/usr.sbin/i4b/g711conv/g711conv.1
@@ -0,0 +1,92 @@
+.\"
+.\" Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: g711conv.1,v 1.3 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:54:33 1999]
+.\"
+.Dd March 15, 1999
+.Dt G711CONV 1
+.Os
+.Sh NAME
+.Nm g711conv
+.Nd conversions according to G.711
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl u
+.Op Fl P
+.Op Fl A
+.Op Fl R
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is part of the isdn4bsd package and is used to convert between the A-Law and
+u-law formats as specified in ITU G.711. It is based on a freely available
+and freely usable reference implementation done by Sun Microsystems, Inc.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl a
+Convert A-law to u-law
+.It Fl u
+Convert u-law to A-law
+.It Fl r
+Reverse bits before conversion
+.It Fl R
+Reverse bits after conversion
+.It Fl P
+Print the resulting conversion tables (as C-source) to stdout instead of
+doing the actual conversion.
+.El
+.Sh STANDARDS
+A-Law and u-Law conversions are specified in ITU Recommendation G.711.
+.Pp
+The reference implementation done by Sun Microsystems, Inc. is available
+from http://www.itu.int/itudoc/itu-t/rec/g/g700-799/refimpl.txt
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+g711conv -P -a
+.Ed
+.Pp
+prints out the A-law to u-law conversion table as C-source to stdout.
+.Pp
+The command:
+.Bd -literal -offset indent
+cat max_headroom.ul | g711conv -u -R > /dev/i4btel0
+.Ed
+.Pp
+converts the u-law coded voice of Max Headroom to A-law, reverses the
+bits of the result and moves that to an active isdn4bsd telephone connection.
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org
+based on the G.711 conversion reference code written by Sun Microsystems,
+Inc. and code contributed to isdn4bsd by Stefan Bethke.
diff --git a/usr.sbin/i4b/g711conv/g711conv.c b/usr.sbin/i4b/g711conv/g711conv.c
new file mode 100644
index 0000000..f8087c0
--- /dev/null
+++ b/usr.sbin/i4b/g711conv/g711conv.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 A-law to u-law and u-law to A-law conversion routines and tables
+ * were taken from the G.711 reference implementation from Sun and freely
+ * available as http://www.itu.int/itudoc/itu-t/rec/g/g700-799/refimpl.txt.
+ *
+ * Therefore for that part of the code, the following restrictions apply:
+ *
+ *
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE 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 source code 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 THIS SOFTWARE
+ * 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
+ *
+ * ---
+ *
+ * The bitreverse table was contributed by Stefan Bethke.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * A-law / u-law conversions as specified in G.711
+ * -----------------------------------------------
+ *
+ * last edit-date: [Mon Dec 13 21:44:01 1999]
+ *
+ * $Id: g711conv.c,v 1.5 1999/12/13 21:25:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <machine/i4b_ioctl.h>
+
+/* copy from CCITT G.711 specifications */
+
+/* u- to A-law conversions */
+
+unsigned char _u2a[128] = {
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128
+};
+
+/* A- to u-law conversions */
+
+unsigned char _a2u[128] = {
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127
+};
+
+/* reverse bits (7->0, 6->1, 5->2 etc) for tx to / rx from ISDN */
+
+unsigned char bitreverse[256] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+/* A-law to u-law conversion */
+
+unsigned char alaw2ulaw(unsigned char aval)
+{
+ aval &= 0xff;
+ return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+
+unsigned char ulaw2alaw(unsigned char uval)
+{
+ uval &= 0xff;
+ return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "g711conv - do conversions according to ITU G.711, (version %d.%d.%d)\n",VERSION, REL, STEP);
+ fprintf(stderr, "usage: g711conv -a -r -R -u -P\n");
+ fprintf(stderr, " -a A-law to u-law conversion\n");
+ fprintf(stderr, " -r reverse bits before conversion\n");
+ fprintf(stderr, " -R reverse bits after conversion\n");
+ fprintf(stderr, " -u u-law to A-law conversion\n");
+ fprintf(stderr, " -P print conversion table as C source\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ int c;
+ int opt_a = 0;
+ int opt_u = 0;
+ int opt_r = 0;
+ int opt_P = 0;
+ int opt_R = 0;
+ unsigned char uc;
+
+ while ((c = getopt(argc, argv, "aurPR")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ opt_a = 1;
+ break;
+
+ case 'u':
+ opt_u = 1;
+ break;
+
+ case 'r':
+ opt_r = 1;
+ break;
+
+ case 'R':
+ opt_R = 1;
+ break;
+
+ case 'P':
+ opt_P = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if((opt_a + opt_u) > 1)
+ usage();
+
+ if(opt_P)
+ {
+ printf("\n/* ");
+
+ if((opt_a + opt_u) == 0)
+ printf("No Conversion");
+
+ if(opt_a)
+ printf("A-law to u-law conversion");
+
+ if(opt_u)
+ printf("u-law to A-law conversion");
+
+ if(opt_r)
+ printf(", reverse bits BEFORE conversion");
+
+ if(opt_R)
+ printf(", reverse bits AFTER conversion");
+
+ if(opt_a)
+ {
+ printf(" */\n\nunsigned char a2u_tab[256] = {");
+ }
+ else if(opt_u)
+ {
+ printf(" */\n\nunsigned char u2a_tab[256] = {");
+ }
+ else
+ {
+ printf(" */\n\nunsigned char table[256] = {");
+ }
+
+ for(i=0; i < 256; i++)
+ {
+ uc = i;
+
+ if(!(i % 8))
+ printf("\n/* %02x */\t", i);
+
+ if(opt_r)
+ uc = bitreverse[uc];
+
+ if(opt_u)
+ uc = ulaw2alaw(uc);
+
+ if(opt_a)
+ uc = alaw2ulaw(uc);
+
+ if(opt_R)
+ uc = bitreverse[uc];
+
+ if(i == 255)
+ printf("0x%02x", uc);
+ else
+ printf("0x%02x, ", uc);
+ }
+ printf("\n};\n");
+ }
+ else
+ {
+ unsigned char ib[1];
+
+ while(fread(ib, 1, 1, stdin) == 1)
+ {
+ if(opt_r)
+ ib[0] = bitreverse[ib[0]];
+
+ if(opt_u)
+ ib[0] = ulaw2alaw(ib[0]);
+
+ if(opt_a)
+ ib[0] = alaw2ulaw(ib[0]);
+
+ if(opt_R)
+ ib[0] = bitreverse[ib[0]];
+
+ fwrite(ib, 1, 1, stdout);
+ }
+ }
+ return(0);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/Makefile b/usr.sbin/i4b/isdnd/Makefile
new file mode 100644
index 0000000..bc4fc38
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/Makefile
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+PROG= isdnd
+MAN= isdnd.rc.5 isdnd.rates.5 isdnd.acct.5 isdnd.8
+SRCS= rc_parse.y rc_scan.l main.c rc_config.c log.c curses.c \
+ process.c rates.c msghdl.c fsm.c support.c timer.c \
+ exec.c dial.c monitor.c pcause.c controller.c alias.c \
+ y.tab.h holiday.c
+
+# compile debug support
+COPTS+= -DDEBUG
+
+# avoid wacky merging of string constants from
+# source code with compile-time timestamp
+COPTS+= -fno-merge-constants
+
+# enable rtprio usage
+COPTS+= -DUSE_RTPRIO
+
+COPTS+= -I. -I${.CURDIR}/../isdnmonitor -I${.CURDIR}/../isdntel -I${.CURDIR}
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.if !defined(I4B_WITHOUT_CURSES)
+COPTS+= -DUSE_CURSES
+DPADD= ${LIBCURSES}
+LDADD= -lcurses
+.endif
+
+.if defined(I4B_EXTERNAL_MONITOR)
+COPTS+= -DI4B_EXTERNAL_MONITOR
+.if defined(I4B_NOTCPIP_MONITOR)
+COPTS+= -DI4B_NOTCPIP_MONITOR
+.endif
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdnd/alias.c b/usr.sbin/i4b/isdnd/alias.c
new file mode 100644
index 0000000..a775fc9
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/alias.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnd - common aliasfile handling
+ * =================================
+ *
+ * NOTE: this has to stay in sync with isdntel/alias.c to be able
+ * to share a common aliasfile!
+ *
+ * $Id: alias.c,v 1.8 1999/12/13 21:25:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:45:19 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static struct alias *firsta = NULL;
+
+#define MAXBUFSZ 256
+
+static void free_alias(struct alias *ptr);
+
+/*---------------------------------------------------------------------------*
+ * read in and init aliases
+ *---------------------------------------------------------------------------*/
+void
+init_alias(char *filename)
+{
+ FILE *fp;
+ unsigned char buffer[MAXBUFSZ + 1];
+ unsigned char number[MAXBUFSZ + 1];
+ unsigned char name[MAXBUFSZ + 1];
+ unsigned char *s, *d;
+ struct alias *newa = NULL;
+ struct alias *lasta = NULL;
+
+ firsta = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ log(LL_ERR, "init_alias: error opening aliasfile %s: %s!", filename, strerror(errno));
+ exit(1);
+ }
+
+ while((fgets(buffer, MAXBUFSZ, fp)) != NULL)
+ {
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ s = buffer;
+ d = number;
+
+ while(*s && (isdigit(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ while(*s && (isspace(*s)))
+ s++;
+
+ d = name;
+
+ while(*s && (isprint(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((strlen(number) > 1) && (strlen(name) > 1))
+ {
+ if((newa = (struct alias *) malloc(sizeof(struct alias))) == NULL)
+ {
+ log(LL_ERR, "init_alias: malloc failed for struct alias!\n");
+ exit(1);
+ }
+
+ if((newa->number = (char *) malloc(strlen(number)+1)) == NULL)
+ {
+ log(LL_ERR, "init_alias: malloc failed for number alias!\n");
+ exit(1);
+ }
+
+ if((newa->name = (char *) malloc(strlen(name)+1)) == NULL)
+ {
+ log(LL_ERR, "init_alias: malloc failed for name alias!\n");
+ exit(1);
+ }
+
+ strcpy(newa->name, name);
+ strcpy(newa->number, number);
+ newa->next = NULL;
+
+ if(firsta == NULL)
+ {
+ firsta = newa;
+ }
+ else
+ {
+ lasta->next = newa;
+ }
+ lasta = newa;
+ }
+ }
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * free all aliases
+ *---------------------------------------------------------------------------*/
+void
+free_aliases(void)
+{
+ free_alias(firsta);
+}
+
+/*---------------------------------------------------------------------------*
+ * free aliases
+ *---------------------------------------------------------------------------*/
+static void
+free_alias(struct alias *ptr)
+{
+
+ if(ptr == NULL)
+ return;
+
+ if(ptr->next != NULL)
+ free_alias(ptr->next);
+
+ if(ptr->number != NULL)
+ free(ptr->number);
+
+ if(ptr->name != NULL)
+ free(ptr->name);
+
+ free(ptr);
+}
+
+/*---------------------------------------------------------------------------*
+ * try to find alias for number. if no alias found, return number.
+ *---------------------------------------------------------------------------*/
+char *
+get_alias(char *number)
+{
+ struct alias *ca = NULL;
+
+ if(firsta == NULL)
+ return(number);
+
+ ca = firsta;
+
+ for(;;)
+ {
+ if(strlen(number) == strlen(ca->number))
+ {
+ if(!(strcmp(number, ca->number)))
+ return(ca->name);
+ }
+ if(ca->next == NULL)
+ break;
+ ca = ca->next;
+ }
+ return(number);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/config.h b/usr.sbin/i4b/isdnd/config.h
new file mode 100644
index 0000000..a4b05f1
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/config.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - compile time configuration header file
+ * ---------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon May 21 11:21:15 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/* general values */
+
+#define UMASK 022 /* file creation perm mask */
+#define CFG_ENTRY_MAX 60 /* max no of config entries */
+#define ISDN_CTRL_MAX 4 /* max no of controllers */
+#define MAX_RE 8 /* max regular expression entries */
+
+/* monitor max values */
+
+#define MAX_MHOSTS 8 /* max allowed monitor hosts */
+
+/* timouts */
+
+#define TIMEOUT_CONNECT_ACTIVE 30 /* seconds to wait for MSG_CONN_ACT */
+
+/* utility programs forked */
+
+#define REGPROG_DEF "program" /* default program to use for regexpr */
+#define ANSWERPROG_DEF "answer" /* default telephone answer program */
+
+#endif /* _CONFIG_H_ */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/controller.c b/usr.sbin/i4b/isdnd/controller.c
new file mode 100644
index 0000000..16a60c8
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/controller.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - controller state support routines
+ * ----------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Oct 21 11:02:15 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "isdnd.h"
+
+static int
+init_controller_state(int controller, int ctrl_type, int card_type, int tei, int nbch);
+
+/*---------------------------------------------------------------------------*
+ * get name of a controller
+ *---------------------------------------------------------------------------*/
+const char *
+name_of_controller(int ctrl_type, int card_type)
+{
+ static char *passive_card[] = {
+ "Teles S0/8",
+ "Teles S0/16",
+ "Teles S0/16.3",
+ "AVM A1 or Fritz!Card",
+ "Teles S0/16.3 PnP",
+ "Creatix S0 PnP",
+ "USRobotics Sportster ISDN TA",
+ "Dr. Neuhaus NICCY Go@",
+ "Sedlbauer win speed",
+ "Dynalink IS64PH",
+ "ISDN Master, MasterII or Blaster",
+ "AVM PCMCIA Fritz!Card",
+ "ELSA QuickStep 1000pro/ISA",
+ "ELSA QuickStep 1000pro/PCI",
+ "Siemens I-Talk",
+ "ELSA MicroLink ISDN/MC",
+ "ELSA MicroLink MCall",
+ "ITK ix1 micro",
+ "AVM Fritz!Card PCI",
+ "ELSA PCC-16",
+ "AVM Fritz!Card PnP",
+ "Siemens I-Surf 2.0 PnP",
+ "Asuscom ISDNlink 128K PnP",
+ "ASUSCOM P-IN100-ST-D (Winbond W6692)",
+ "Teles S0/16.3c PnP",
+ "AcerISDN P10 PnP",
+ "TELEINT ISDN SPEED No. 1",
+ "Cologne Chip HFC-S PCI based",
+ "Traverse Tech NETjet-S / Teles PCI-TJ",
+ "Eicon.Diehl DIVA 2.0 / 2.02 ISA PnP",
+ "Compaq Microcom 610",
+ "AVM Fritz!Card PCI Version 2",
+ };
+
+ static char *daic_card[] = {
+ "EICON.Diehl S",
+ "EICON.Diehl SX/SXn",
+ "EICON.Diehl SCOM",
+ "EICON.Diehl QUADRO",
+ };
+
+ static char *capi_card[] = {
+ "AVM T1 PCI",
+ "AVM B1 PCI",
+ "AVM B1 ISA",
+ };
+
+ if(ctrl_type == CTRL_PASSIVE)
+ {
+ int index = card_type - CARD_TYPEP_8;
+ if (index >= 0 && index < (sizeof passive_card / sizeof passive_card[0]))
+ return passive_card[index];
+ }
+ else if(ctrl_type == CTRL_DAIC)
+ {
+ int index = card_type - CARD_TYPEA_DAIC_S;
+ if (index >= 0 && index < (sizeof daic_card / sizeof daic_card[0] ))
+ return daic_card[index];
+ }
+ else if(ctrl_type == CTRL_TINADD)
+ {
+ return "Stollmann tina-dd";
+ }
+ else if(ctrl_type == CTRL_CAPI)
+ {
+ int index = card_type - CARD_TYPEC_AVM_T1_PCI;
+ if (index >= 0 && index < (sizeof capi_card / sizeof capi_card[0] ))
+ return capi_card[index];
+ }
+
+ return "unknown card type";
+}
+
+/*---------------------------------------------------------------------------*
+ * init controller state array
+ *---------------------------------------------------------------------------*/
+void
+init_controller(void)
+{
+ int i;
+ int max = 1;
+ msg_ctrl_info_req_t mcir;
+
+ for(i=0; i < max; i++)
+ {
+ mcir.controller = i;
+
+ if((ioctl(isdnfd, I4B_CTRL_INFO_REQ, &mcir)) < 0)
+ {
+ log(LL_ERR, "init_controller: ioctl I4B_CTRL_INFO_REQ failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ if((ncontroller = max = mcir.ncontroller) == 0)
+ {
+ log(LL_ERR, "init_controller: no ISDN controller found!");
+ do_exit(1);
+ }
+
+ if(mcir.ctrl_type == -1 || mcir.card_type == -1)
+ {
+ log(LL_ERR, "init_controller: ctrl/card is invalid!");
+ do_exit(1);
+ }
+
+ /* init controller tab */
+
+ if((init_controller_state(i, mcir.ctrl_type, mcir.card_type, mcir.tei, mcir.nbch)) == ERROR)
+ {
+ log(LL_ERR, "init_controller: init_controller_state for controller %d failed", i);
+ do_exit(1);
+ }
+ }
+ DBGL(DL_RCCF, (log(LL_DBG, "init_controller: found %d ISDN controller(s)", max)));
+}
+
+/*--------------------------------------------------------------------------*
+ * init controller state table entry
+ *--------------------------------------------------------------------------*/
+static int
+init_controller_state(int controller, int ctrl_type, int card_type, int tei,
+ int nbch)
+{
+ int i;
+
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "init_controller_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ /* init controller tab */
+
+ switch (ctrl_type) {
+ case CTRL_PASSIVE:
+ if((card_type > CARD_TYPEP_UNK) &&
+ (card_type <= CARD_TYPEP_MAX))
+ {
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = card_type;
+ isdn_ctrl_tab[controller].state = CTRL_UP;
+ }
+ else
+ {
+ log(LL_ERR, "init_controller_state: unknown card type %d", card_type);
+ return(ERROR);
+ }
+ break;
+
+ case CTRL_DAIC:
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = card_type;
+ isdn_ctrl_tab[controller].state = CTRL_DOWN;
+ break;
+
+ case CTRL_TINADD:
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = 0;
+ isdn_ctrl_tab[controller].state = CTRL_DOWN;
+ break;
+
+ case CTRL_CAPI:
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = card_type;
+ isdn_ctrl_tab[controller].state = CTRL_UP;
+ break;
+
+ default:
+ log(LL_ERR, "init_controller_state: unknown controller type %d", ctrl_type);
+ return(ERROR);
+ }
+
+ isdn_ctrl_tab[controller].nbch = nbch;
+ isdn_ctrl_tab[controller].freechans = nbch;
+ for (i = 0; i < nbch; i++)
+ isdn_ctrl_tab[controller].stateb[i] = CHAN_IDLE;
+ isdn_ctrl_tab[controller].tei = tei;
+ isdn_ctrl_tab[controller].l1stat = LAYER_IDLE;
+ isdn_ctrl_tab[controller].l2stat = LAYER_IDLE;
+
+ log(LL_DMN, "init_controller_state: controller %d is %s",
+ controller,
+ name_of_controller(isdn_ctrl_tab[controller].ctrl_type,
+ isdn_ctrl_tab[controller].card_type));
+
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * init active or capi controller
+ *--------------------------------------------------------------------------*/
+void
+init_active_controller(void)
+{
+ int ret;
+ int unit = 0;
+ int controller;
+ char cmdbuf[MAXPATHLEN+128];
+
+ for(controller = 0; controller < ncontroller; controller++)
+ {
+ if(isdn_ctrl_tab[controller].ctrl_type == CTRL_TINADD)
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "init_active_controller, tina-dd %d: executing [%s %d]", unit, tinainitprog, unit)));
+
+ snprintf(cmdbuf, sizeof(cmdbuf), "%s %d", tinainitprog, unit);
+
+ if((ret = system(cmdbuf)) != 0)
+ {
+ log(LL_ERR, "init_active_controller, tina-dd %d: %s returned %d!", unit, tinainitprog, ret);
+ do_exit(1);
+ }
+ }
+
+ /*
+ * Generic microcode loading. If a controller has
+ * defined a microcode file, load it using the
+ * I4B_CTRL_DOWNLOAD ioctl.
+ */
+
+ if(isdn_ctrl_tab[controller].firmware != NULL)
+ {
+ int fd, ret;
+ struct isdn_dr_prot idp;
+ struct isdn_download_request idr;
+
+ fd = open(isdn_ctrl_tab[controller].firmware, O_RDONLY);
+ if (fd < 0) {
+ log(LL_ERR, "init_active_controller %d: open %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ idp.bytecount = lseek(fd, 0, SEEK_END);
+ idp.microcode = mmap(0, idp.bytecount, PROT_READ,
+ MAP_SHARED, fd, 0);
+ if (idp.microcode == MAP_FAILED) {
+ log(LL_ERR, "init_active_controller %d: mmap %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "init_active_controller %d: loading firmware from [%s]", controller, isdn_ctrl_tab[controller].firmware)));
+
+ idr.controller = controller;
+ idr.numprotos = 1;
+ idr.protocols = &idp;
+
+ ret = ioctl(isdnfd, I4B_CTRL_DOWNLOAD, &idr, sizeof(idr));
+ if (ret) {
+ log(LL_ERR, "init_active_controller %d: load %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ munmap(idp.microcode, idp.bytecount);
+ close(fd);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * init controller D-channel ISDN protocol
+ *--------------------------------------------------------------------------*/
+void
+init_controller_protocol(void)
+{
+ int controller;
+ msg_prot_ind_t mpi;
+
+ for(controller = 0; controller < ncontroller; controller++)
+ {
+ mpi.controller = controller;
+ mpi.protocol = isdn_ctrl_tab[controller].protocol;
+
+ if((ioctl(isdnfd, I4B_PROT_IND, &mpi)) < 0)
+ {
+ log(LL_ERR, "init_controller_protocol: ioctl I4B_PROT_IND failed: %s", strerror(errno));
+ do_exit(1);
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * set controller state to UP/DOWN
+ *--------------------------------------------------------------------------*/
+int
+set_controller_state(int controller, int state)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_controller_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if(state == CTRL_UP)
+ {
+ isdn_ctrl_tab[controller].state = CTRL_UP;
+ DBGL(DL_CNST, (log(LL_DBG, "set_controller_state: controller [%d] set UP!", controller)));
+ }
+ else if (state == CTRL_DOWN)
+ {
+ isdn_ctrl_tab[controller].state = CTRL_DOWN;
+ DBGL(DL_CNST, (log(LL_DBG, "set_controller_state: controller [%d] set DOWN!", controller)));
+ }
+ else
+ {
+ log(LL_ERR, "set_controller_state: invalid controller state [%d]!", state);
+ return(ERROR);
+ }
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * get controller state
+ *--------------------------------------------------------------------------*/
+int
+get_controller_state(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_controller_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ return(isdn_ctrl_tab[controller].state);
+}
+
+/*--------------------------------------------------------------------------*
+ * decrement number of free channels for controller
+ *--------------------------------------------------------------------------*/
+int
+decr_free_channels(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "decr_free_channels: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ if(isdn_ctrl_tab[controller].freechans > 0)
+ {
+ (isdn_ctrl_tab[controller].freechans)--;
+ DBGL(DL_CNST, (log(LL_DBG, "decr_free_channels: ctrl %d, now %d chan free", controller, isdn_ctrl_tab[controller].freechans)));
+ return(GOOD);
+ }
+ else
+ {
+ log(LL_ERR, "decr_free_channels: controller [%d] already 0 free chans!", controller);
+ return(ERROR);
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * increment number of free channels for controller
+ *--------------------------------------------------------------------------*/
+int
+incr_free_channels(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "incr_free_channels: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ if(isdn_ctrl_tab[controller].freechans < isdn_ctrl_tab[controller].nbch)
+ {
+ (isdn_ctrl_tab[controller].freechans)++;
+ DBGL(DL_CNST, (log(LL_DBG, "incr_free_channels: ctrl %d, now %d chan free", controller, isdn_ctrl_tab[controller].freechans)));
+ return(GOOD);
+ }
+ else
+ {
+ log(LL_ERR, "incr_free_channels: controller [%d] already %d free chans!", controller, isdn_ctrl_tab[controller].nbch);
+ return(ERROR);
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * get number of free channels for controller
+ *--------------------------------------------------------------------------*/
+int
+get_free_channels(int controller)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "get_free_channels: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+ DBGL(DL_CNST, (log(LL_DBG, "get_free_channels: ctrl %d, %d chan free", controller, isdn_ctrl_tab[controller].freechans)));
+ return(isdn_ctrl_tab[controller].freechans);
+}
+
+/*--------------------------------------------------------------------------*
+ * set channel state to busy
+ *--------------------------------------------------------------------------*/
+int
+set_channel_busy(int controller, int channel)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_channel_busy: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
+ {
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ }
+
+ if(isdn_ctrl_tab[controller].stateb[channel] == CHAN_RUN)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B%d already busy!", controller, channel+1)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb[channel] = CHAN_RUN;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B%d set to BUSY!", controller, channel+1)));
+ }
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * set channel state to idle
+ *--------------------------------------------------------------------------*/
+int
+set_channel_idle(int controller, int channel)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "set_channel_idle: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
+ {
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ }
+
+ if (isdn_ctrl_tab[controller].stateb[channel] == CHAN_IDLE)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B%d already idle!", controller, channel+1)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb[channel] = CHAN_IDLE;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B%d set to IDLE!", controller, channel+1)));
+ }
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * return channel state
+ *--------------------------------------------------------------------------*/
+int
+ret_channel_state(int controller, int channel)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "ret_channel_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
+ {
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ }
+
+ return(isdn_ctrl_tab[controller].stateb[channel]);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/curses.c b/usr.sbin/i4b/isdnd/curses.c
new file mode 100644
index 0000000..19e7af7
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/curses.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - curses fullscreen output
+ * -------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:33 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef USE_CURSES
+
+#include "isdnd.h"
+
+#define CHPOS(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused)
+
+static void display_budget(void);
+static void display_cards(void);
+static void menuexit(WINDOW *menu_w);
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_screen(void)
+{
+ char buffer[512];
+ int uheight, lheight;
+ int i, j;
+ cfg_entry_t *p;
+
+ initscr(); /* curses init */
+
+ if((COLS < 80) || (LINES < 24))
+ {
+ log(LL_ERR, "ERROR, minimal screensize must be 80x24, is %dx%d, terminating!",COLS, LINES);
+ do_exit(1);
+ }
+
+ noecho();
+ raw();
+
+ uheight = ncontroller * 2; /* cards * b-channels */
+ lheight = LINES - uheight - 6 + 1; /* rest of display */
+
+ if((upper_w = newwin(uheight, COLS, UPPER_B, 0)) == NULL)
+ {
+ log(LL_ERR, "ERROR, curses init upper window, terminating!");
+ exit(1);
+ }
+
+ if((mid_w = newwin(1, COLS, UPPER_B+uheight+1, 0)) == NULL)
+ {
+ log(LL_ERR, "ERROR, curses init mid window, terminating!");
+ exit(1);
+ }
+
+ if((lower_w = newwin(lheight, COLS, UPPER_B+uheight+3, 0)) == NULL)
+ {
+ log(LL_ERR, "ERROR, curses init lower window, LINES = %d, lheight = %d, uheight = %d, terminating!", LINES, lheight, uheight);
+ exit(1);
+ }
+
+ scrollok(lower_w, 1);
+
+ snprintf(buffer, sizeof(buffer), "----- isdn controller channel state ------------- isdnd %02d.%02d.%d [pid %d] -", VERSION, REL, STEP, (int)getpid());
+
+ while(strlen(buffer) < COLS && strlen(buffer) < sizeof(buffer) - 1)
+ strcat(buffer, "-");
+
+ move(0, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ move(1, 0);
+ /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */
+ addstr("c tei b remote iface dir outbytes obps inbytes ibps units");
+
+ snprintf(buffer, sizeof(buffer), "----- isdn userland interface state ------------------------------------------");
+ while(strlen(buffer) < COLS && strlen(buffer) < sizeof(buffer) - 1)
+ strcat(buffer, "-");
+
+ move(uheight+2, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ snprintf(buffer, sizeof(buffer), "----- isdnd logfile display --------------------------------------------------");
+ while(strlen(buffer) < COLS && strlen(buffer) < sizeof(buffer) - 1)
+ strcat(buffer, "-");
+
+ move(uheight+4, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ refresh();
+
+ for(i=0, j=0; i <= ncontroller; i++, j+=2)
+ {
+ if(isdn_ctrl_tab[i].tei == -1)
+ mvwprintw(upper_w, j, H_CNTL, "%d --- 1 ", i);
+ else
+ mvwprintw(upper_w, j, H_CNTL, "%d %3d 1 ", i, isdn_ctrl_tab[i].tei);
+ mvwprintw(upper_w, j+1, H_CNTL, " L12 2 ");
+ }
+ wrefresh(upper_w);
+
+ for(i=0, j=0; i < nentries; i++) /* walk thru all entries */
+ {
+ p = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ mvwprintw(mid_w, 0, j, "%s%d ", bdrivername(p->usrdevicename), p->usrdeviceunit);
+
+ p->fs_position = j;
+
+ j += ((strlen(bdrivername(p->usrdevicename)) + (p->usrdeviceunit > 9 ? 2 : 1) + 1));
+ }
+ wrefresh(mid_w);
+
+ wmove(lower_w, 0, 0);
+ wrefresh(lower_w);
+
+ curses_ready = 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "1 - (D)isplay refresh",
+ "2 - (H)angup (choose a channel)",
+ "3 - (R)eread config file",
+ "4 - (S)how card types",
+ "5 - (B)udget information",
+ "6 - (Q)uit the program",
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+ fd_set set;
+ struct timeval timeout;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init menu window!");
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(menu_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ goto mexit;
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case ' ':
+ case '\t': /* hilite next option */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case ('0'+WBUDGET+1): /* display budget info */
+ case 'B':
+ case 'b':
+ display_budget();
+ goto mexit;
+
+ case ('0'+WREFRESH+1): /* display refresh */
+ case 'D':
+ case 'd':
+ wrefresh(curscr);
+ goto mexit;
+
+ case ('0'+WQUIT+1): /* quit program */
+ case 'Q':
+ case 'q':
+ menuexit(menu_w);
+ do_exit(0);
+ goto mexit;
+
+ case ('0'+WHANGUP+1): /* hangup connection */
+ case 'H':
+ case 'h':
+ display_chans();
+ goto mexit;
+
+ case ('0'+WREREAD+1): /* reread config file */
+ case 'R':
+ case 'r':
+ rereadconfig(42);
+ goto mexit;
+
+ case ('0'+WSHOW+1): /* reread config file */
+ case 'S':
+ case 's':
+ display_cards();
+ goto mexit;
+
+ case '\n':
+ case '\r': /* exec highlighted option */
+ switch(mpos)
+ {
+ case WREFRESH:
+ wrefresh(curscr);
+ break;
+
+ case WQUIT:
+ menuexit(menu_w);
+ do_exit(0);
+ break;
+
+ case WHANGUP:
+ display_chans();
+ break;
+
+ case WREREAD:
+ rereadconfig(42);
+ break;
+
+ case WBUDGET:
+ display_budget();
+ break;
+
+ case WSHOW:
+ display_cards();
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ menuexit(menu_w);
+}
+
+static void
+menuexit(WINDOW *menu_w)
+{
+ int uheight = ncontroller * 2; /* cards * b-channels */
+ char buffer[512];
+
+ /* delete the menu window */
+
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(mid_w);
+ wrefresh(mid_w);
+
+ touchwin(lower_w);
+ wrefresh(lower_w);
+
+ touchwin(upper_w);
+ wrefresh(upper_w);
+
+ move(1, 0);
+ /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */
+ addstr("c tei b remote iface dir outbytes obps inbytes ibps units");
+
+ sprintf(buffer, "----- isdn userland interface state ------------------------------------------");
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+2, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ sprintf(buffer, "----- isdnd logfile display --------------------------------------------------");
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+4, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * display the charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_charge(cfg_entry_t *cep)
+{
+ mvwprintw(upper_w, CHPOS(cep), H_UNITS, "%d", cep->charge);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display the calculated charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_ccharge(cfg_entry_t *cep, int units)
+{
+ mvwprintw(upper_w, CHPOS(cep), H_UNITS, "(%d)", units);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display accounting information
+ *---------------------------------------------------------------------------*/
+void
+display_acct(cfg_entry_t *cep)
+{
+ mvwprintw(upper_w, CHPOS(cep), H_OUT, "%-10d", cep->outbytes);
+ mvwprintw(upper_w, CHPOS(cep), H_OUTBPS, "%-4d", cep->outbps);
+ mvwprintw(upper_w, CHPOS(cep), H_IN, "%-10d", cep->inbytes);
+ mvwprintw(upper_w, CHPOS(cep), H_INBPS, "%-4d", cep->inbps);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display connect information
+ *---------------------------------------------------------------------------*/
+void
+display_connect(cfg_entry_t *cep)
+{
+ char buffer[256];
+
+ /* remote telephone number */
+
+ if(aliasing)
+ {
+ if(cep->direction == DIR_IN)
+ snprintf(buffer, sizeof(buffer), "%s", get_alias(cep->real_phone_incoming.number));
+ else
+ snprintf(buffer, sizeof(buffer), "%s", get_alias(cep->remote_phone_dialout.number));
+ }
+ else
+ {
+ if(cep->direction == DIR_IN)
+ snprintf(buffer, sizeof(buffer), "%s/%s", cep->name, cep->real_phone_incoming.number);
+ else
+ snprintf(buffer, sizeof(buffer), "%s/%s", cep->name, cep->remote_phone_dialout.number);
+ }
+
+ buffer[H_IFN - H_TELN - 1] = '\0';
+
+ mvwprintw(upper_w, CHPOS(cep), H_TELN, "%s", buffer);
+
+ /* interface */
+
+ mvwprintw(upper_w, CHPOS(cep), H_IFN, "%s%d ",
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ mvwprintw(upper_w, CHPOS(cep), H_IO,
+ cep->direction == DIR_OUT ? "out" : "in");
+
+ mvwprintw(upper_w, CHPOS(cep), H_OUT, "-");
+ mvwprintw(upper_w, CHPOS(cep), H_OUTBPS, "-");
+ mvwprintw(upper_w, CHPOS(cep), H_IN, "-");
+ mvwprintw(upper_w, CHPOS(cep), H_INBPS, "-");
+
+ if(do_bell)
+ display_bell();
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * erase line at disconnect time
+ *---------------------------------------------------------------------------*/
+void
+display_disconnect(cfg_entry_t *cep)
+{
+ wmove(upper_w, CHPOS(cep),
+ H_TELN);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+
+ if(do_bell)
+ display_bell();
+
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_updown(cfg_entry_t *cep, int updown)
+{
+ if(updown)
+ wstandend(mid_w);
+ else
+ wstandout(mid_w);
+
+ mvwprintw(mid_w, 0, cep->fs_position, "%s%d ",
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ wstandend(mid_w);
+ wrefresh(mid_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_l12stat(int controller, int layer, int state)
+{
+ if(controller > ncontroller)
+ return;
+ if(!(layer == 1 || layer == 2))
+ return;
+
+ if(state)
+ wstandout(upper_w);
+ else
+ wstandend(upper_w);
+
+ if(layer == 1)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+ if(!state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ }
+ else if(layer == 2)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ if(state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+ }
+
+ wstandend(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display TEI
+ *---------------------------------------------------------------------------*/
+void
+display_tei(int controller, int tei)
+{
+ if(controller > ncontroller)
+ return;
+
+ if(tei == -1)
+ mvwprintw(upper_w, controller*2, H_TEI, "---");
+ else
+ mvwprintw(upper_w, controller*2, H_TEI, "%3d", tei);
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display bell :-)
+ *---------------------------------------------------------------------------*/
+void
+display_bell(void)
+{
+ static char bell[1] = { 0x07 };
+ write(STDOUT_FILENO, &bell[0], 1);
+}
+
+/*---------------------------------------------------------------------------*
+ * display channel information for shutdown
+ *---------------------------------------------------------------------------*/
+void
+display_chans(void)
+{
+ char buffer[80];
+ int i;
+ int cnt = 0;
+ WINDOW *chan_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+ cfg_entry_t *cep = NULL;
+
+ /* need this later to close the connection */
+ struct ctlr_chan {
+ int cntl;
+ int chn;
+ } *cc = NULL;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if((get_controller_state(i)) != CTRL_UP)
+ continue;
+ if((ret_channel_state(i, CHAN_B1)) == CHAN_RUN)
+ cnt++;
+ if((ret_channel_state(i, CHAN_B2)) == CHAN_RUN)
+ cnt++;
+ }
+
+ if(cnt > 0)
+ {
+ if ((cc = (struct ctlr_chan *)malloc (cnt *
+ sizeof (struct ctlr_chan))) == NULL)
+ {
+ return;
+ }
+ nlines = cnt + 4;
+ ncols = 60;
+ }
+ else
+ {
+ nlines = 5;
+ ncols = 22;
+ }
+
+ pos_y = WMENU_POSLN + 4;
+ pos_x = WMENU_POSCO + 10;
+
+ /* create a new window in the lower screen area */
+
+ if((chan_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init channel window!");
+ if (cnt > 0)
+ free(cc);
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(chan_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(chan_w);
+ mvwaddstr(chan_w, 0, (ncols / 2) - (strlen("Channels") / 2), "Channels");
+ wstandend(chan_w);
+
+ /* no active channels */
+ if (cnt == 0)
+ {
+ mvwaddstr(chan_w, 2, 2, "No active channels");
+ wrefresh(chan_w);
+ sleep(1);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+ return;
+ }
+
+ nlines = 2;
+ ncols = 1;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if((get_controller_state(i)) != CTRL_UP)
+ continue;
+
+ if((ret_channel_state(i, CHAN_B1)) == CHAN_RUN)
+ {
+ snprintf(buffer, sizeof(buffer), "%d - Controller %d channel %s", ncols, i, "B1");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B1;
+ nlines++;
+ ncols++;
+ }
+ if((ret_channel_state(i, CHAN_B2)) == CHAN_RUN)
+ {
+ snprintf(buffer, sizeof(buffer), "%d - Controller %d channel %s", ncols, i, "B2");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B2;
+ nlines++;
+ ncols++;
+ }
+ }
+
+ for(;;)
+ {
+ wrefresh(chan_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ break;
+
+ ncols = wgetch(chan_w);
+
+ if (!(isdigit(ncols)))
+ {
+ display_bell();
+ continue;
+ }
+
+ nlines = ncols - '0';
+
+ if ((nlines == 0) || (nlines > cnt))
+ {
+ display_bell();
+ continue;
+ }
+
+ if((cep = get_cep_by_cc(cc[nlines-1].cntl, cc[nlines-1].chn))
+ != NULL)
+ {
+ log(LL_CHD, "%05d %s manual disconnect (fullscreen menu)", cep->cdid, cep->name);
+ cep->hangup = 1;
+ break;
+ }
+ }
+
+ free(cc);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display card type information
+ *---------------------------------------------------------------------------*/
+static void
+display_cards(void)
+{
+ WINDOW *chan_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+ int i;
+
+ nlines = 6+ncontroller;
+ ncols = 60;
+ pos_y = WMENU_POSLN;
+ pos_x = WMENU_POSCO;
+
+ /* create a new window in the lower screen area */
+
+ if((chan_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init channel window!");
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(chan_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(chan_w);
+ mvwaddstr(chan_w, 0, (ncols / 2) - (strlen("Cards") / 2), "Cards");
+ wstandend(chan_w);
+
+ mvwprintw(chan_w, 2, 2, "ctrl description");
+ mvwprintw(chan_w, 3, 2, "---- ----------------------------------------------");
+ for (i = 0; i < ncontroller; i++)
+ {
+ mvwprintw(chan_w, 4+i, 2, " #%d %s", i,
+ name_of_controller(isdn_ctrl_tab[i].ctrl_type,
+ isdn_ctrl_tab[i].card_type));
+ }
+
+ wrefresh(chan_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT*2;
+ timeout.tv_usec = 0;
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ {
+ delwin(chan_w);
+ return;
+ }
+
+ wgetch(chan_w);
+ delwin(chan_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display budget info
+ *---------------------------------------------------------------------------*/
+static void
+display_budget(void)
+{
+ WINDOW *bud_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+ int i, j;
+ cfg_entry_t *cep;
+ time_t now;
+ double uptime;
+ int minutes;
+ int hours;
+ int days;
+
+ nlines = 0;
+ ncols = 73;
+ pos_y = WMENU_POSLN;
+ pos_x = WMENU_POSCO-3;
+
+ for(i=0, j=0; i < nentries; i++) /* walk thru all entries */
+ {
+ cep = &cfg_entry_tab[i]; /* get ptr to entry */
+
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ nlines++;
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ nlines++;
+ }
+
+ if(nlines == 0)
+ return;
+
+ nlines += 6;
+
+ /* create a new window in the lower screen area */
+
+ if((bud_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ log(LL_WRN, "ERROR, curses init budget window!");
+ return;
+ }
+
+ now = time(NULL);
+ uptime = difftime(now, starttime);
+
+ minutes = (time_t) (uptime / 60) % 60;
+ hours = (time_t) (uptime / (60*60)) % (60*60);
+ days = (time_t) (uptime / (60*60*24)) % (60*60*24);
+
+ uptime = uptime / (60*60);
+
+ /* create a border around the window */
+
+ box(bud_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(bud_w);
+ mvwaddstr(bud_w, 0, (ncols / 2) - (strlen("Budget") / 2), "Budget");
+ wstandend(bud_w);
+
+ mvwprintw(bud_w, 1, 2, "isdnd uptime: %d %s - %d %s - %d %s",
+ days,
+ days == 1 ? "day" : "days",
+ hours,
+ hours == 1 ? "hour" : "hours",
+ minutes,
+ minutes == 1 ? "minute" : "minutes");
+
+ mvwprintw(bud_w, 2, 2, "name t period rest ncall rest rqsts /hr rdone /hr rrjct /hr ");
+ mvwprintw(bud_w, 3, 2, "-------- - ------ ------ ----- ----- ----- ---- ----- ---- ----- ----");
+
+ for(i=0, j=4; i < nentries; i++) /* walk thru all entries */
+ {
+ cep = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ mvwprintw(bud_w, j, 2, "%-8s %c %-6d %-6ld %-5d %-5d %-5d %-4.1f %-5d %-4.1f %-5d %-4.1f",
+ cep->name,
+ 'o',
+ cep->budget_calloutperiod,
+ (long)(cep->budget_calloutperiod_time - now),
+ cep->budget_calloutncalls,
+ cep->budget_calloutncalls_cnt,
+ cep->budget_callout_req,
+ (double)cep->budget_callout_req / uptime,
+ cep->budget_callout_done,
+ (double)cep->budget_callout_done / uptime,
+ cep->budget_callout_rej,
+ (double)cep->budget_callout_rej / uptime);
+ j++;
+ }
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ {
+ mvwprintw(bud_w, j, 2, "%-8s %c %-6d %-6ld %-5d %-5d %-5d %-4.1f %-5d %-4.1f %-5d %-4.1f",
+ (cep->budget_calloutperiod && cep->budget_calloutncalls) ? "" : cep->name,
+ 'b',
+ cep->budget_callbackperiod,
+ (long)(cep->budget_callbackperiod_time - now),
+ cep->budget_callbackncalls,
+ cep->budget_callbackncalls_cnt,
+ cep->budget_callback_req,
+ (double)cep->budget_callback_req / uptime,
+ cep->budget_callback_done,
+ (double)cep->budget_callback_done / uptime,
+ cep->budget_callback_rej,
+ (double)cep->budget_callback_rej / uptime);
+ j++;
+ }
+ }
+
+ wrefresh(bud_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT*3;
+ timeout.tv_usec = 0;
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ {
+ delwin(bud_w);
+ return;
+ }
+
+ wgetch(bud_w);
+ delwin(bud_w);
+}
+
+#endif
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/dial.c b/usr.sbin/i4b/isdnd/dial.c
new file mode 100644
index 0000000..5ccbd33
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/dial.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - dial handling routines
+ * -----------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:19 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+/*---------------------------------------------------------------------------*
+ * select the first remote number to dial according to the
+ * dial strategy
+ *---------------------------------------------------------------------------*/
+void
+select_first_dialno(cfg_entry_t *cep)
+{
+ int i, j;
+
+ if(cep->keypad[0] != '\0')
+ return;
+
+ if(cep->remote_numbers_count < 1)
+ {
+ log(LL_ERR, "select_first_dialno: remote_numbers_count < 1!");
+ return;
+ }
+
+ if(cep->remote_numbers_count == 1)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[0].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: only one no, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+ return;
+ }
+
+ if(cep->remote_numbers_handling == RNH_FIRST)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[0].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use first, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+ return;
+ }
+
+ i = cep->last_remote_number;
+
+ for(j = cep->remote_numbers_count; j > 0; j--)
+ {
+ if(cep->remote_numbers[i].flag == RNF_SUCC)
+ {
+ if(cep->remote_numbers_handling == RNH_LAST)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[i].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[i].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use last, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = i;
+ return;
+ }
+ else
+ {
+ if(++i >= cep->remote_numbers_count)
+ i = 0;
+
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[i].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[i].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use next, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = i;
+ return;
+ }
+ }
+
+ if(++i >= cep->remote_numbers_count)
+ i = 0;
+ }
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: no last found (use 0), no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * select next remote number to dial (last was unsuccesfull)
+ *---------------------------------------------------------------------------*/
+void
+select_next_dialno(cfg_entry_t *cep)
+{
+ if(cep->remote_numbers_count < 1)
+ {
+ log(LL_ERR, "select_next_dialno: remote_numbers_count < 1!");
+ return;
+ }
+
+ if(cep->remote_numbers_count == 1)
+ {
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[0].number);
+ strcpy(cep->remote_phone_dialout.subaddr, cep->remote_numbers[0].subaddr);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_next_dialno: only one no, no = %s", cep->remote_phone_dialout.number)));
+ cep->last_remote_number = 0;
+ return;
+ }
+
+ /* mark last try as bad */
+
+ cep->remote_numbers[cep->last_remote_number].flag = RNF_IDLE;
+
+ /* next one to try */
+
+ cep->last_remote_number++;
+
+ if(cep->last_remote_number >= cep->remote_numbers_count)
+ cep->last_remote_number = 0;
+
+ strcpy(cep->remote_phone_dialout.number, cep->remote_numbers[cep->last_remote_number].number);
+
+ DBGL(DL_DIAL, (log(LL_DBG, "select_next_dialno: index=%d, no=%s",
+ cep->last_remote_number,
+ cep->remote_numbers[cep->last_remote_number].number)));
+}
+
+/*---------------------------------------------------------------------------*
+ * dial succeded, store this number as the last successful
+ *---------------------------------------------------------------------------*/
+void
+select_this_dialno(cfg_entry_t *cep)
+{
+ cep->remote_numbers[cep->last_remote_number].flag = RNF_SUCC;
+
+ DBGL(DL_DIAL, (log(LL_DBG, "select_this_dialno: index = %d, no = %s",
+ cep->last_remote_number,
+ cep->remote_numbers[cep->last_remote_number].number)));
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/exec.c b/usr.sbin/i4b/isdnd/exec.c
new file mode 100644
index 0000000..0c1254f
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/exec.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * exec.h - supplemental program/script execution
+ * ----------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:46 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <paths.h>
+
+#define MAX_PIDS 32
+
+static struct pid_tab {
+ pid_t pid;
+ cfg_entry_t *cep;
+} pid_tab[MAX_PIDS];
+
+/*---------------------------------------------------------------------------*
+ * SIGCHLD signal handler
+ *---------------------------------------------------------------------------*/
+void
+sigchild_handler(int sig)
+{
+ int retstat;
+ register int i;
+ pid_t pid;
+
+ if((pid = waitpid(-1, &retstat, WNOHANG)) <= 0)
+ {
+ log(LL_ERR, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
+ error_exit(1, "ERROR, sigchild_handler, waitpid: %s", strerror(errno));
+ }
+ else
+ {
+ if(WIFEXITED(retstat))
+ {
+ DBGL(DL_PROC, (log(LL_DBG, "normal child (pid=%d) termination, exitstat = %d",
+ pid, WEXITSTATUS(retstat))));
+ }
+ else if(WIFSIGNALED(retstat))
+ {
+ if(WCOREDUMP(retstat))
+ log(LL_WRN, "child (pid=%d) termination due to signal %d (coredump)",
+ pid, WTERMSIG(retstat));
+ else
+ log(LL_WRN, "child (pid=%d) termination due to signal %d",
+ pid, WTERMSIG(retstat));
+ }
+ }
+
+ /* check if hangup required */
+
+ for(i=0; i < MAX_PIDS; i++)
+ {
+ if(pid_tab[i].pid == pid)
+ {
+ if(pid_tab[i].cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_PROC, (log(LL_DBG, "sigchild_handler: scheduling hangup for cdid %d, pid %d",
+ pid_tab[i].cep->cdid, pid_tab[i].pid)));
+ pid_tab[i].cep->hangup = 1;
+ }
+ pid_tab[i].pid = 0;
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * execute prog as a subprocess and pass an argumentlist
+ *---------------------------------------------------------------------------*/
+pid_t
+exec_prog(char *prog, char **arglist)
+{
+ char tmp[MAXPATHLEN];
+ char path[MAXPATHLEN];
+ pid_t pid;
+ int a;
+
+ snprintf(path, sizeof(path), "%s/%s", ETCPATH, prog);
+
+ arglist[0] = path;
+
+ tmp[0] = '\0';
+
+ for(a=1; arglist[a] != NULL; ++a )
+ {
+ strcat(tmp, " " );
+ strcat(tmp, arglist[a]);
+ }
+
+ DBGL(DL_PROC, (log(LL_DBG, "exec_prog: %s, args:%s", path, tmp)));
+
+ switch(pid = fork())
+ {
+ case -1: /* error */
+ log(LL_ERR, "ERROR, exec_prog/fork: %s", strerror(errno));
+ error_exit(1, "ERROR, exec_prog/fork: %s", strerror(errno));
+ case 0: /* child */
+ break;
+ default: /* parent */
+ return(pid);
+ }
+
+ /* this is the child now */
+
+ /*
+ * close files used only by isdnd, e.g.
+ * 1. /dev/i4b
+ * 2. /var/log/isdnd.acct (or similar, when used)
+ * 3. /var/log/isdnd.log (or similar, when used)
+ */
+ close(isdnfd);
+
+ if(useacctfile && acctfp)
+ fclose(acctfp);
+
+ if(uselogfile && logfp)
+ fclose(logfp);
+
+ if(execvp(path,arglist) < 0 )
+ _exit(127);
+
+ return(-1);
+}
+
+/*---------------------------------------------------------------------------*
+ * run interface up/down script
+ *---------------------------------------------------------------------------*/
+int
+exec_connect_prog(cfg_entry_t *cep, const char *prog, int link_down)
+{
+ char *argv[32], **av = argv;
+ char devicename[MAXPATHLEN], addr[100];
+ char *device;
+ int s;
+ struct ifreq ifr;
+
+ /* the obvious things */
+ device = bdrivername(cep->usrdevicename);
+ snprintf(devicename, sizeof(devicename), "%s%d", device, cep->usrdeviceunit);
+ *av++ = (char*)prog;
+ *av++ = "-d";
+ *av++ = devicename;
+ *av++ = "-f";
+ *av++ = link_down ? "down" : "up";
+
+ /* try to figure AF_INET address of interface */
+ addr[0] = '\0';
+ memset(&ifr, 0, sizeof ifr);
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpy(ifr.ifr_name, devicename, sizeof(ifr.ifr_name));
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s >= 0) {
+ if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) >= 0) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
+ strcpy(addr, inet_ntoa(sin->sin_addr));
+ *av++ = "-a";
+ *av++ = addr;
+ }
+ close(s);
+ }
+
+ /* terminate argv */
+ *av++ = NULL;
+
+ return exec_prog((char*)prog, argv);
+}
+
+/*---------------------------------------------------------------------------*
+ * run answeringmachine application
+ *---------------------------------------------------------------------------*/
+int
+exec_answer(cfg_entry_t *cep)
+{
+ char *argv[32];
+ u_char devicename[MAXPATHLEN];
+ int pid;
+ char *device;
+
+ device = bdrivername(cep->usrdevicename);
+
+ snprintf(devicename, sizeof(devicename), "%si4b%s%d", _PATH_DEV, device,
+ cep->usrdeviceunit);
+
+ argv[0] = cep->answerprog;
+ argv[1] = "-D";
+ argv[2] = devicename;
+ argv[3] = "-d";
+ argv[4] = "unknown";
+ argv[5] = "-s";
+ argv[6] = "unknown";
+ argv[7] = NULL;
+
+ /* if destination telephone number avail, add it as argument */
+
+ if(*cep->local_phone_incoming.number)
+ argv[4] = cep->local_phone_incoming.number;
+
+ /* if source telephone number avail, add it as argument */
+
+ if(*cep->real_phone_incoming.number)
+ argv[6] = cep->real_phone_incoming.number;
+
+ if(*cep->display)
+ {
+ argv[7] = "-t";
+ argv[8] = cep->display;
+ argv[9] = NULL;
+ }
+
+ /* exec program */
+
+ DBGL(DL_PROC, (log(LL_DBG, "exec_answer: prog=[%s]", cep->answerprog)));
+
+ pid = exec_prog(cep->answerprog, argv);
+
+ /* enter pid and conf ptr entry addr into table */
+
+ if(pid != -1)
+ {
+ int i;
+
+ for(i=0; i < MAX_PIDS; i++)
+ {
+ if(pid_tab[i].pid == 0)
+ {
+ pid_tab[i].pid = pid;
+ pid_tab[i].cep = cep;
+ break;
+ }
+ }
+ return(GOOD);
+ }
+ return(ERROR);
+}
+
+/*---------------------------------------------------------------------------*
+ * check if a connection has an outstanding process, if yes, kill it
+ *---------------------------------------------------------------------------*/
+void
+check_and_kill(cfg_entry_t *cep)
+{
+ int i;
+
+ for(i=0; i < MAX_PIDS; i++)
+ {
+ if(pid_tab[i].cep == cep)
+ {
+ pid_t kp;
+
+ DBGL(DL_PROC, (log(LL_DBG, "check_and_kill: killing pid %d", pid_tab[i].pid)));
+
+ kp = pid_tab[i].pid;
+ pid_tab[i].pid = 0;
+ kill(kp, SIGHUP);
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * update budget callout/callback statistics counter file
+ *---------------------------------------------------------------------------*/
+void
+upd_callstat_file(char *filename, int rotateflag)
+{
+ FILE *fp;
+ time_t s, l, now;
+ int n;
+ int ret;
+
+ now = time(NULL);
+
+ fp = fopen(filename, "r+");
+
+ if(fp == NULL)
+ {
+ /* file not there, create it and exit */
+
+ log(LL_WRN, "upd_callstat_file: creating %s", filename);
+
+ fp = fopen(filename, "w");
+ if(fp == NULL)
+ {
+ log(LL_ERR, "ERROR, upd_callstat_file: cannot create %s, %s", filename, strerror(errno));
+ return;
+ }
+
+ ret = fprintf(fp, "%ld %ld 1", now, now);
+ if(ret <= 0)
+ log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
+
+ fclose(fp);
+ return;
+ }
+
+ /* get contents */
+
+ ret = fscanf(fp, "%ld %ld %d", &s, &l, &n);
+
+ /* reset fp */
+
+ rewind(fp);
+
+ if(ret != 3)
+ {
+ /* file corrupt ? anyway, initialize */
+
+ log(LL_WRN, "upd_callstat_file: initializing %s", filename);
+
+ s = l = now;
+ n = 0;
+ }
+
+ if(rotateflag)
+ {
+ struct tm *stmp;
+ int dom;
+
+ /* get day of month for last timestamp */
+ stmp = localtime(&l);
+ dom = stmp->tm_mday;
+
+ /* get day of month for just now */
+ stmp = localtime(&now);
+
+ if(dom != stmp->tm_mday)
+ {
+ FILE *nfp;
+ char buf[MAXPATHLEN];
+
+ /* new day, write last days stats */
+
+ sprintf(buf, "%s-%02d", filename, stmp->tm_mday);
+
+ nfp = fopen(buf, "w");
+ if(nfp == NULL)
+ {
+ log(LL_ERR, "ERROR, upd_callstat_file: cannot open for write %s, %s", buf, strerror(errno));
+ return;
+ }
+
+ ret = fprintf(nfp, "%ld %ld %d", s, l, n);
+ if(ret <= 0)
+ log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
+
+ fclose(nfp);
+
+ /* init new days stats */
+ n = 0;
+ s = now;
+
+ log(LL_WRN, "upd_callstat_file: rotate %s, new s=%ld l=%ld n=%d", filename, s, l, n);
+ }
+ }
+
+ n++; /* increment call count */
+
+ /*
+ * the "%-3d" is necessary to overwrite any
+ * leftovers from previous contents!
+ */
+
+ ret = fprintf(fp, "%ld %ld %-3d", s, now, n);
+
+ if(ret <= 0)
+ log(LL_ERR, "ERROR, upd_callstat_file: fprintf failed: %s", strerror(errno));
+
+ fclose(fp);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/fsm.c b/usr.sbin/i4b/isdnd/fsm.c
new file mode 100644
index 0000000..e555fa8
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/fsm.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * FSM for isdnd
+ * -------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:35:56 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+/* table of state descriptions */
+
+static char *state_text[N_STATES] = {
+ "idle",
+ "dialing",
+ "waitdialretry",
+ "dialretry",
+
+ "pcb-dialing",
+ "pcb-dialfail",
+ "pcb-waitcall",
+
+ "acb-waitdisc",
+ "acb-waitdial",
+ "acb-dialing",
+ "acb-dialfail",
+
+ "accepted",
+ "connected",
+ "waitdisconnect",
+ "down",
+ "alert",
+
+ "Illegal State"
+};
+
+/* table of event descriptions */
+
+static char *event_text[N_EVENTS] = {
+
+ /* incoming messages */
+
+ "msg-con-ind",
+ "msg-con-act-ind",
+ "msg-disc-ind",
+ "msg-dialout",
+
+ /* local events */
+
+ "timeout",
+ "disconnect-req",
+ "callback-req",
+ "alert-req",
+
+ /* illegal */
+
+ "Illegal Event"
+};
+
+/*---------------------------------------------------------------------------*
+ * illegal state default action
+ *---------------------------------------------------------------------------*/
+static void
+F_ill(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_ill: Illegal State reached !!!")));
+}
+
+/*---------------------------------------------------------------------------*
+ * No change, No action
+ *---------------------------------------------------------------------------*/
+static void
+F_NcNa(cfg_entry_t *cep)
+{
+}
+
+/*---------------------------------------------------------------------------*
+ * incoming CONNECT, accepting call
+ *---------------------------------------------------------------------------*/
+static void
+F_MCI(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_MCI: tx SETUP_RESP_ACCEPT")));
+ sendm_connect_resp(cep, cep->cdid, SETUP_RESP_ACCEPT, 0);
+ start_timer(cep, TIMEOUT_CONNECT_ACTIVE);
+}
+
+/*---------------------------------------------------------------------------*
+ * incoming connect active, call is now active
+ *---------------------------------------------------------------------------*/
+static void
+F_MCAI(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_MCAI: Connection active!")));
+
+ stop_timer(cep);
+
+ if((cep->dialin_reaction == REACT_ANSWER) &&
+ (cep->b1protocol == BPROT_NONE))
+ {
+ exec_answer(cep);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout
+ *---------------------------------------------------------------------------*/
+static void
+F_TIMO(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_TIMO: Timout occured!")));
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * incoming disconnect indication
+ *---------------------------------------------------------------------------*/
+static void
+F_IDIS(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_IDIS: disconnect indication")));
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * local disconnect request
+ *---------------------------------------------------------------------------*/
+static void
+F_DRQ(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_DRQ: local disconnect request")));
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+}
+
+/*---------------------------------------------------------------------------*
+ * disconnect indication after local disconnect req
+ *---------------------------------------------------------------------------*/
+static void
+F_MDI(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_MDI: disconnect indication, local disconnected")));
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * local requested outgoing dial
+ *---------------------------------------------------------------------------*/
+static void
+F_DIAL(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_DIAL: local dial out request")));
+
+ if(cep->dialrandincr)
+ cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
+
+ cep->dial_count = 0;
+
+ select_first_dialno(cep);
+
+ sendm_connect_req(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * outgoing dial successfull
+ *---------------------------------------------------------------------------*/
+static void
+F_DOK(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_DOK: dial out ok")));
+ select_this_dialno(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * outgoing dial fail (ST_SUSE !!!)
+ *---------------------------------------------------------------------------*/
+static void
+F_DFL(cfg_entry_t *cep)
+{
+ cep->last_release_time = time(NULL);
+
+ if(cep->dialouttype == DIALOUT_NORMAL)
+ {
+ cep->dial_count++;
+
+ if(cep->dial_count < cep->dialretries || cep->dialretries == -1)
+ {
+ /* inside normal retry cycle */
+
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial fail, dial retry")));
+ select_next_dialno(cep);
+ cep->cdid = CDID_RESERVED;
+ cep->state = ST_DIALRTMRCHD;
+ return;
+ }
+
+ /* retries exhausted */
+
+ if(!cep->usedown)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry fail, dial retries exhausted")));
+ dialresponse(cep, DSTAT_TFAIL);
+ cep->cdid = CDID_UNUSED;
+ cep->dial_count = 0;
+ cep->state = ST_IDLE;
+ return;
+ }
+
+ /* interface up/down active */
+
+ cep->down_retry_count++;
+
+ if(cep->down_retry_count > cep->downtries)
+ {
+ /* set interface down */
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, setting interface down!")));
+ dialresponse(cep, DSTAT_PFAIL);
+ if_down(cep);
+ cep->state = ST_DOWN;
+ }
+ else
+ {
+ /* enter new dial retry cycle */
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: dial retry cycle fail, enter new retry cycle!")));
+ select_next_dialno(cep);
+ cep->state = ST_DIALRTMRCHD;
+ }
+
+ cep->dial_count = 0;
+ cep->cdid = CDID_RESERVED;
+ }
+ else /* cdp->dialouttype == DIALOUT_CALLEDBACK */
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "F_DFL: calledback dial done, wait for incoming call")));
+ cep->cdid = CDID_RESERVED;
+ cep->state = ST_PCB_WAITCALL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * local requested outgoing dial
+ *---------------------------------------------------------------------------*/
+static void
+F_ACBW(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBW: local callback, wait callback recovery time")));
+
+ if(cep->dialrandincr)
+ cep->randomtime = (random() & RANDOM_MASK) + cep->recoverytime;
+
+ cep->dial_count = 0;
+
+ cep->cdid = CDID_RESERVED;
+}
+
+/*---------------------------------------------------------------------------*
+ * active callback dialout retry (ST_SUSE !!!)
+ *---------------------------------------------------------------------------*/
+static void
+F_ACBR(cfg_entry_t *cep)
+{
+ cep->dial_count++;
+
+ if(cep->dial_count < cep->dialretries || cep->dialretries == -1)
+ {
+ /* inside normal retry cycle */
+
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial fail, dial retry")));
+ select_next_dialno(cep);
+ cep->cdid = CDID_RESERVED;
+ cep->state = ST_ACB_DIALFAIL;
+ return;
+ }
+
+ /* retries exhausted */
+
+ if(!cep->usedown)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry fail, dial retries exhausted")));
+ dialresponse(cep, DSTAT_TFAIL);
+ cep->cdid = CDID_UNUSED;
+ cep->dial_count = 0;
+ cep->state = ST_IDLE;
+ return;
+ }
+
+ /* interface up/down active */
+
+ cep->down_retry_count++;
+
+ if(cep->down_retry_count > cep->downtries)
+ {
+ /* set interface down */
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, setting interface down!")));
+ dialresponse(cep, DSTAT_PFAIL);
+ if_down(cep);
+ cep->state = ST_DOWN;
+ }
+ else
+ {
+ /* enter new dial retry cycle */
+ DBGL(DL_STATE, (log(LL_DBG, "F_ACBR: dial retry cycle fail, enter new retry cycle!")));
+ select_next_dialno(cep);
+ cep->state = ST_ACB_DIALFAIL;
+ }
+
+ cep->dial_count = 0;
+ cep->cdid = CDID_RESERVED;
+}
+
+/*---------------------------------------------------------------------------*
+ * local requested to send ALERT message
+ *---------------------------------------------------------------------------*/
+static void
+F_ALRT(cfg_entry_t *cep)
+{
+ DBGL(DL_STATE, (log(LL_DBG, "F_ALRT: local send alert request")));
+
+ cep->alert_time = cep->alert;
+
+ sendm_alert_req(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * isdn daemon state transition table
+ *---------------------------------------------------------------------------*/
+struct state_tab {
+ void(*func)(cfg_entry_t *cep); /* function to execute */
+ int newstate; /* next state */
+} state_tab[N_EVENTS][N_STATES] = {
+
+/* STATE: ST_IDLE ST_DIAL ST_DIALRTMRCHD ST_DIALRETRY ST_PCB_DIAL ST_PCB_DIALFAIL ST_PCB_WAITCALL ST_ACB_WAITDISC ST_ACB_WAITDIAL ST_ACB_DIAL ST_ACB_DIALFAIL ST_ACCEPTED ST_CONNECTED ST_WAITDISCI ST_DOWN ST_ALERT ST_ILLEGAL */
+/* -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
+/* messages */
+/* EV_MCI */{{F_MCI, ST_ACCEPTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_MCI, ST_ACCEPTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_MCI, ST_ACCEPTED}, {F_ill, ST_ILL}},
+/* EV_MCAI */{{F_ill, ST_ILL}, {F_DOK, ST_CONNECTED}, {F_ill, ST_ILL}, {F_DOK, ST_CONNECTED}, {F_DOK, ST_CONNECTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_DOK, ST_CONNECTED}, {F_ill, ST_ILL}, {F_MCAI,ST_CONNECTED}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_MDI */{{F_ill, ST_ILL}, {F_DFL, ST_SUSE}, {F_ill, ST_ILL}, {F_DFL, ST_SUSE}, {F_DFL, ST_SUSE}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ACBW,ST_ACB_WAITDIAL},{F_ill, ST_ILL}, {F_ACBR, ST_SUSE}, {F_ACBR,ST_SUSE}, {F_IDIS,ST_IDLE}, {F_IDIS,ST_IDLE}, {F_MDI, ST_IDLE}, {F_ill, ST_ILL}, {F_MDI, ST_IDLE}, {F_ill, ST_ILL}},
+/* EV_MDO */{{F_DIAL,ST_DIAL}, {F_NcNa,ST_DIAL}, {F_NcNa,ST_DIALRTMRCHD},{F_NcNa,ST_DIALRETRY}, {F_NcNa,ST_PCB_DIAL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+
+/* local requests */
+/* EV_TIMO */{{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_TIMO,ST_IDLE}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_DRQ */{{F_NcNa, ST_IDLE}, {F_DRQ, ST_WAITDISCI}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_DRQ, ST_WAITDISCI}, {F_NcNa,ST_WAITDISCI}, {F_NcNa, ST_DOWN}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_CBRQ */{{F_NcNa,ST_ACB_WAITDIAL},{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_NcNa,ST_ACB_WAITDIAL},{F_NcNa, ST_ACB_DIAL}, {F_NcNa,ST_ACB_DIALFAIL},{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+/* EV_ALRT */{{F_ALRT,ST_ALERT}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}},
+
+/* illegal */
+
+/* EV_ILL */{{F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}, {F_ill, ST_ILL}}
+};
+
+/*---------------------------------------------------------------------------*
+ * event handler
+ *---------------------------------------------------------------------------*/
+void
+next_state(cfg_entry_t *cep, int event)
+{
+ int currstate, newstate;
+
+ if(event > N_EVENTS)
+ {
+ log(LL_ERR, "next_state: event > N_EVENTS");
+ error_exit(1, "next_state: event > N_EVENTS");
+ }
+
+ currstate = cep->state;
+
+ if(currstate > N_STATES)
+ {
+ log(LL_ERR, "next_state: currstate > N_STATES");
+ error_exit(1, "next_state: currstate > N_STATES");
+ }
+
+ newstate = state_tab[event][currstate].newstate;
+
+ if(newstate > N_STATES)
+ {
+ log(LL_ERR, "next_state: newstate > N_STATES");
+ error_exit(1, "next_state: newstate > N_STATES");
+ }
+
+ if(newstate != ST_SUSE)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "FSM event [%s]: [%s => %s]", event_text[event],
+ state_text[currstate],
+ state_text[newstate])));
+ }
+
+ (*state_tab[event][currstate].func)(cep);
+
+ if(newstate == ST_ILL)
+ {
+ log(LL_ERR, "FSM ILLEGAL STATE, event=%s: oldstate=%s => newstate=%s]",
+ event_text[event],
+ state_text[currstate],
+ state_text[newstate]);
+ }
+
+ if(newstate == ST_SUSE)
+ {
+ DBGL(DL_STATE, (log(LL_DBG, "FSM (SUSE) event [%s]: [%s => %s]", event_text[event],
+ state_text[currstate],
+ state_text[cep->state])));
+ }
+ else
+ {
+ cep->state = newstate;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return pointer to current state description
+ *---------------------------------------------------------------------------*/
+char *
+printstate(cfg_entry_t *cep)
+{
+ return((char *) state_text[cep->state]);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/holiday.c b/usr.sbin/i4b/isdnd/holiday.c
new file mode 100644
index 0000000..5bf9582
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/holiday.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2000, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnd - holiday file handling
+ * =============================
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed May 2 09:42:56 2001]
+ *
+ * Format:
+ *
+ * day.month.year optional comment (different day every year) or
+ * day.month optional comment (same day every year)
+ *
+ * i.e.:
+ *
+ * 23.4.2000 Ostersonntag
+ * 3.10 Tag der deutschen Einheit
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+struct holiday {
+ int day;
+ int month;
+ int year;
+ struct holiday *next;
+};
+
+static struct holiday *firsth = NULL;
+
+#define MAXBUFSZ 256
+
+static void free_holiday(struct holiday *ptr);
+
+/*---------------------------------------------------------------------------*
+ * read in and init holidayes
+ *---------------------------------------------------------------------------*/
+void
+init_holidays(char *filename)
+{
+ FILE *fp;
+ unsigned char buffer[MAXBUFSZ + 1];
+ struct holiday *newh = NULL;
+ struct holiday *lasth = NULL;
+ int ret;
+ int day, month, year;
+
+ firsth = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "init_holiday: error opening holidayfile %s: %s!", filename, strerror(errno))));
+ return;
+ }
+
+ while((fgets(buffer, MAXBUFSZ, fp)) != NULL)
+ {
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ ret = sscanf(buffer, "%d.%d.%d", &day, &month, &year);
+
+ if(ret != 3)
+ {
+ ret = sscanf(buffer, "%d.%d", &day, &month);
+ if(ret != 2)
+ {
+ log(LL_ERR, "init_holiday: parse error for string [%s]!", buffer);
+ exit(1);
+ }
+ year = 0;
+ }
+
+ if((newh = (struct holiday *) malloc(sizeof(struct holiday))) == NULL)
+ {
+ log(LL_ERR, "init_holiday: malloc failed for struct holiday!\n");
+ exit(1);
+ }
+
+ if(year)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "init_holidays: add %d.%d.%d", day, month, year)));
+ }
+ else
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "init_holidays: add %d.%d", day, month)));
+ }
+
+ newh->day = day;
+ newh->month = month;
+ newh->year = year;
+ newh->next = NULL;
+
+ if(firsth == NULL)
+ {
+ firsth = newh;
+ }
+ else
+ {
+ lasth->next = newh;
+ }
+ lasth = newh;
+ }
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * free all holidays
+ *---------------------------------------------------------------------------*/
+void
+free_holidays(void)
+{
+ free_holiday(firsth);
+}
+
+/*---------------------------------------------------------------------------*
+ * free holidayes
+ *---------------------------------------------------------------------------*/
+static void
+free_holiday(struct holiday *ptr)
+{
+
+ if(ptr == NULL)
+ return;
+
+ if(ptr->next != NULL)
+ free_holiday(ptr->next);
+
+ free(ptr);
+}
+
+/*---------------------------------------------------------------------------*
+ * check if date/month/year is a holiday
+ *---------------------------------------------------------------------------*/
+int
+isholiday(int d, int m, int y)
+{
+ struct holiday *ch = NULL;
+
+ if(firsth == NULL)
+ return(0);
+
+ ch = firsth;
+
+ for(;;)
+ {
+ if(ch->day == d && ch->month == m)
+ {
+ if(ch->year == 0)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isholiday: %d.%d is a holiday!", d, m)));
+ return(1);
+ }
+ else if(ch->year == y)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isholiday: %d.%d.%d is a holiday!", d, m, y)));
+ return(1);
+ }
+ }
+
+ if(ch->next == NULL)
+ break;
+
+ ch = ch->next;
+ }
+ return(0);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/isdnd.8 b/usr.sbin/i4b/isdnd/isdnd.8
new file mode 100644
index 0000000..42c01f1
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.8
@@ -0,0 +1,428 @@
+.\"
+.\" Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdnd.8,v 1.29 2000/05/02 11:50:28 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Wed May 2 10:48:30 2001]
+.\"
+.Dd May 2, 2001
+.Dt ISDND 8
+.Os
+.Sh NAME
+.Nm isdnd
+.Nd isdn4bsd ISDN connection management daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl d Ar debuglevel
+.Op Fl f
+.Op Fl F
+.Op Fl l
+.Op Fl L Ar logfile
+.Op Fl P
+.Op Fl r Ar device
+.Op Fl s Ar facility
+.Op Fl t Ar terminaltype
+.Op Fl u Ar charging unit length
+.Op Fl m
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the isdn4bsd package daemon which manages all ISDN related connection
+and disconnection of ISDN devices supported by the package.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Use
+.Ar configfile
+as the name of the runtime configuration filename for
+.Nm
+instead of the default file
+.Li /etc/isdn/isdnd.rc .
+.It Fl d
+If debugging support is compiled into
+.Nm
+this option is used to specify the debugging level, or better which kind
+of debugging messages are displayed.
+The debugging level is the sum of the
+following values:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar 0x001
+general debugging.
+.It Ar 0x002
+rates calculation.
+.It Ar 0x004
+timing calculations.
+.It Ar 0x008
+state transitions.
+.It Ar 0x010
+retry handling.
+.It Ar 0x020
+dialing.
+.It Ar 0x040
+process handling.
+.It Ar 0x080
+isdn4bsd kernel i/o calls.
+.It Ar 0x100
+controller and channel busy/free messages.
+.It Ar 0x200
+isdnd.rc configuration file processing.
+.It Ar 0x400
+outgoing call budget handling.
+.It Ar 0x800
+valid keyword and holiday file processing.
+.El
+.Pp
+The value can be specified in any number base supported by the
+.Xr sscanf 3
+library routine.
+.Pp
+In addition, this option accepts also the character 'n' as an argument to
+disable displaying debug messages on the full-screen display.
+.Pp
+.It Fl f
+Specifying this option causes
+.Nm
+to enter the full-screen mode of operation.
+When operating in this mode,
+entering the control character
+.Em Control-L
+causes the display to be refreshed and entering
+.Em Carriage-Return
+or
+.Em Enter
+will pop-up a command window.
+Because the
+.Nm
+utility will not listen to messages while the command window is active,
+this command window will disappear automatically after 5 seconds without
+any command key press.
+.Pp
+While the command window is active,
+.Em Tab
+or
+.Em Space
+advances to the next menu item.
+To execute a command, press
+.Em Return
+or
+.Em Enter
+for the highlighted menu item, or enter the number corresponding to the
+item to be executed or enter the capitalized character in the menu item
+description.
+.It Fl l
+If this option is set, logging is not done via the
+.Xr syslogd 8
+facility but instead is appended to a file.
+.It Fl L
+Specifies the name of the logfile which is used when the option
+.Em -l
+is set.
+See also the keyword
+.Em rotatesuffix
+in the system section of
+.Xr isdnd.rc 5 .
+.It Fl P
+This option prints out the parsed and verified isdnd configuration in the same
+format as the isdnd.rc file.
+This output can be used as an isdnd.rc file.
+This
+feature is especially useful when debugging an isdnd.rc file to see, what the
+default settings of options are when they are not set in the isdnd.rc input
+file.
+.Pp
+The
+.Nm
+exits after the printout is done.
+.It Fl F
+This option prevents
+.Nm
+to detach from the controlling tty and become a daemon.
+.It Fl r
+In conjunction with the
+.Fl t
+option,
+.Ar device
+specifies a terminal device which becomes the controlling tty for
+.Nm
+and on which the full-screen mode output is displayed.
+.It Fl s
+This option may be used to specify the logging facility in case
+.Xr syslog 3
+logging is configured and another facility than the default LOCAL0
+facility shall be used.
+The facility is to be specified as an integer in
+the range 0-11 or 16-23 (see the file /usr/include/syslog.h).
+.It Fl t
+In conjunction with the
+.Fl f
+and
+.Fl r
+options,
+.Ar terminaltype
+specifies a terminal type or termcap entry name (such as vt220) for the device
+used for
+.Nm
+full-screen output.
+This is useful if an unused (no getty running) tty line is
+used for full-screen output for which no
+.Li TERM
+environment variable exists.
+.It Fl u
+Specifies the length of a charging unit in case the config file entry
+keyword
+.Em unitlenghtsrc
+is set to
+.Em cmdl .
+.It Fl m
+If the isdn daemon is compiled with local or remote monitoring support,
+this option disables all monitoring access.
+It overrides the config
+file option
+.Em monitor-allowed .
+.El
+.Sh INTERACTION WITH THE KERNEL
+The
+.Nm
+utility
+communicates with the kernel part of isdn4bsd by receiving status and
+event messages
+.Xr ( read 2
+from device
+.Pa /dev/i4b )
+and by transmitting commands and responses
+.Xr ( ioctl 2
+from device
+.Pa /dev/i4b ) .
+.Pp
+The messages and message parameters are documented in the include
+file
+.Em /usr/include/machine/i4b_ioctl.h .
+.Pp
+Supported command and response messages (ioctls) to the kernel are:
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I4B_CDID_REQ
+Request a unique Call Description IDentifier (cdid) which identifies
+uniquely a single interaction of the local D channel with the exchange.
+.It Ar I4B_CONNECT_REQ
+Actively request a call setup to a remote ISDN subscriber.
+.It Ar I4B_CONNECT_RESP
+Respond to an incoming call, either accept, reject or ignore it.
+.It Ar I4B_DISCONNECT_REQ
+Actively terminate a connection.
+.It Ar I4B_CTRL_INFO_REQ
+Request information about an installed ISDN controller card.
+.It Ar I4B_DIALOUT_RESP
+Give information about call setup to driver who requested dialing out.
+.It Ar I4B_TIMEOUT_UPD
+Update the kernels timeout value(s) in case of dynamically calculated
+shorthold mode timing changes.
+.It Ar I4B_UPDOWN_IND
+Inform the kernel userland drivers about interface soft up/down status
+changes.
+.It Ar I4B_CTRL_DOWNLOAD
+Download firmware to active card(s).
+.It Ar I4B_ACTIVE_DIAGNOSTIC
+Return diagnostic information from active cards.
+.El
+.Pp
+.Pp
+Supported status and event messages from the kernel are:
+.Bl -tag -width Ds -compact -offset indent
+.It Ar MSG_CONNECT_IND
+An incoming call from a remote ISDN user is indicated.
+.It Ar MSG_CONNECT_ACTIVE_IND
+After an incoming call has been accepted locally or an outgoing call has
+been accepted by a remote, the exchange signaled an active connection
+and the corresponding B-channel is switched through.
+.It Ar MSG_DISCONNECT_IND
+A call was terminated.
+.It Ar MSG_DIALOUT_IND
+A userland interface driver requests the daemon to dial out (typically a
+network interface when a packet arrives in its send queue).
+.It Ar MSG_IDLE_TIMEOUT_IND
+A call was terminated by the isdn4bsd kernel driver because a B-channel
+idle timeout occurred.
+.It Ar MSG_ACCT_IND
+Accounting information from a network driver.
+.It Ar MSG_CHARGING_IND
+Charging information from the kernel.
+.El
+.Pp
+.Ss OUTGOING CALLS
+Currently the only possibility to trigger an outgoing call is that an
+isdn4bsd network driver
+.Em (ipr<n>)
+sends a
+.Em MSG_DIALOUT_IND
+to the
+.Nm
+utility.
+.Pp
+The daemon requests a new CDID from the kernel by using the
+.Em I4B_CDID_REQ
+ioctl message, this CDID is now used in all interactions with the kernel
+to identify this single call until a disconnect occurs.
+.Pp
+After getting the CDID, the daemon looks up several additional information
+in its entry section of the configuration corresponding to that connection
+and issues a
+.Em I4B_CONNECT_REQ
+ioctl message to the kernel.
+The kernel now dials the remote side and
+if the remote side accepts the call, the kernel sends a
+.Em MSG_CONNECT_ACTIVE_IND
+to the daemon.
+.Pp
+The call is terminated by either the local side timing out or the remote
+side hanging up the connection or the local side actively sending a
+.Em I4B_DISCONNECT_REQ
+ioctl message, both events are signaled to the
+.Nm
+by the kernel sending the
+.Em I4B_DISCONNECT_IND
+message and the CDID corresponding to the call is no longer valid.
+.Pp
+.Ss INCOMING CALLS
+Incoming calls are signaled to the
+.Nm
+by the kernel transmitting the
+.Em MSG_CONNECT_IND
+message to the daemon.
+.Pp
+With the information contained in this message, the
+.Nm
+searches the entry section of its configuration database and if a match is
+found, it accepts or rejects the call or, if no match is found, it ignores the
+call - all by issuing a
+.Em I4B_CONNECT_RESP
+ioctl message with the appropriate parameters to the kernel.
+.Pp
+In case the daemon decided to accept the call, the kernel signals this
+by sending a
+.Em MSG_CONNECT_ACTIVE_IND
+message to the daemon.
+.Pp
+The call is terminated by either the local side timing out or the remote
+side hanging up the connection or the local side actively sending a
+.Em I4B_DISCONNECT_REQ
+ioctl message, both events are signaled to the
+.Nm
+by the kernel sending the
+.Em I4B_DISCONNECT_IND
+message and the CDID corresponding to the call is no longer valid.
+.Sh SIGNALS
+Sending a HUP signal to
+.Nm
+causes all open connections to be terminated and the configuration file is
+reread.
+In case aliasfile handling was enabled, the aliasfile is also
+reread.
+.Pp
+Sending a USR1 signal to
+.Nm
+causes the accounting file and the logfile (if logging to a file is used
+instead of logging via the
+.Xr syslog 3
+facility) to be closed and reopened to make logfile rotation possible.
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width Ds
+.It Ev TERM
+The terminal type when running in full-screen display mode.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh FILES
+.Bl -tag -width /etc/isdn/isdnd.rates
+.It Pa /dev/i4b
+The device-file used to communicate with the kernel ISDN driver subsystem.
+.It Pa /var/log/messages
+A record of the actions in case of syslogd logging support.
+.It Pa /var/log/isdnd.acct
+The default accounting information filename (if accounting is configured).
+.It Pa /var/log/isdnd.log
+The default logging filename (if logging to a file is configured).
+.It Pa /var/run/isdnd.pid
+The process id of the isdn daemon (also known as "lockfile" to isdnd, preventing multiple invocations of it).
+.It Pa /usr/local/lib/isdn
+.It Pa /etc/isdn
+The directory where isdnd expects some supplementary data files and programs
+for telephone answering support.
+.It Pa /etc/isdn/isdnd.rc
+The default runtime configuration file.
+.It Pa /etc/isdn/isdnd.rates
+The default unit charging rates specification file.
+.It Pa /etc/isdn/isdntel.alias
+The default table (if aliasing is enabled) to convert phone number to caller's name.
+.El
+.Sh EXAMPLES
+For a first try, the following command should be used to start
+.Nm
+in foreground mode for better debugging the configuration setup:
+.Bd -literal -offset indent
+isdnd -d0xf9 -F
+.Ed
+.Pp
+This will start isdnd with reasonable debugging settings and produce
+output on the current terminal.
+The
+.Nm
+utility can then be terminated by entering Control-C.
+.Pp
+Another example, the command:
+.Bd -literal -offset indent
+isdnd -d0xf9 -f -r /dev/ttyv3 -t vt100
+.Ed
+.Pp
+will start
+.Nm
+with reasonable debugging messages enabled, full-screen mode of operation,
+full-screen display redirected to /dev/ttyv3 and using a termcap entry
+for vt100 on this display.
+.Sh DIAGNOSTICS
+Exit status is 0 on success, 1 on error.
+.Sh SEE ALSO
+.Xr i4bing 4 ,
+.Xr i4bipr 4 ,
+.Xr i4bisppp 4 ,
+.Xr isdnd.rates 5 ,
+.Xr isdnd.rc 5 ,
+.Xr isdntel 8 ,
+.Xr isdntrace 8 ,
+.Xr syslogd 8
+.Sh BUGS
+Still one or more left.
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/isdnd/isdnd.acct.5 b/usr.sbin/i4b/isdnd/isdnd.acct.5
new file mode 100644
index 0000000..fdb30d3
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.acct.5
@@ -0,0 +1,107 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdnd.acct.5,v 1.11 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:58:12 1999]
+.\"
+.Dd September 11, 1998
+.Dt ISDND.ACCT 5
+.Os
+.Sh NAME
+.Nm isdnd.acct
+.Nd isdn4bsd ISDN management daemon accounting file format
+.Sh DESCRIPTION
+The file
+.Pa isdnd.acct
+contains accounting information which is written if the variable
+.Em useacctfile
+in the
+.Xr isdnd 8
+configuration file
+.Xr isdnd.rc 5
+is set to
+.Em on
+and charging information transmission has been subscribed for the
+ISDN connection (AOCD or AOCE).
+.Pp
+If the variable
+.Em acctall
+is set to
+.Em on ,
+accounting information is written even if the local site was not charged
+or no charging information is available or is not subscribed.
+.Pp
+The general format of an accounting line is as follows:
+.Pp
+.Dl FROM - UNTIL NAME UNITS (SECONDS) (INBYTES/OUTBYTES)
+.Pp
+.Em FROM
+is the time the connection was established in the format
+.Dl Day.Month.Year Hour:Minutes:seconds
+.Pp
+.Em UNTIL
+is the time the connection was closed. The format is the same as
+described for
+.Em FROM
+above.
+.Pp
+.Em NAME
+is the symbolic name got from the
+.Em name
+entry of the
+.Xr isdnd.rc 5
+config file for this connection.
+.Pp
+.Em UNITS
+is the amount of charging units billed for the connection.
+.Pp
+.Em SECONDS
+is the number of seconds the connection lasted.
+.Pp
+.Em INBYTES
+and
+.Em OUTBYTES
+is the (optional) number of bytes that were transferred.
+.Sh FILES
+.Bl -tag -width /var/log/isdnd.acct -compact
+.It Pa /var/log/isdnd.acct
+The default accounting information file for the
+.Nm isdnd
+ISDN daemon.
+.El
+.Sh EXAMPLES
+This is a typical accounting line:
+.Pp
+.Dl 12.06.97 10:41:37 - 12.06.97 10:45:18 GROGGY 2 (65) (4711/1147)
+.Sh SEE ALSO
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Xr isdnd 8
+daemon and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdnd/isdnd.h b/usr.sbin/i4b/isdnd/isdnd.h
new file mode 100644
index 0000000..e4a0ffc
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.h
@@ -0,0 +1,906 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - main header file
+ * -----------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Aug 11 12:31:44 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _ISDND_H_
+#define _ISDND_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <regex.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef USE_CURSES
+#include <curses.h>
+#endif
+
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <sys/queue.h> /* TAILQ_ macros */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#ifdef USE_RTPRIO
+#include <sys/rtprio.h>
+#endif
+
+#include <net/if_sppp.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_cause.h>
+
+#include "config.h" /* compile time configuration */
+#include "pathnames.h" /* location of files */
+#include "alias.h" /* alias file processing */
+
+/*---------------------------------------------------------------------------*
+ * some general definitions
+ *---------------------------------------------------------------------------*/
+#define GOOD 0 /* general "good" or "ok" return*/
+#define ERROR (-1) /* general error return */
+#define WARNING (-2) /* warning return */
+#define INVALID (-1) /* an invalid integer */
+
+/*---------------------------------------------------------------------------*
+ * misc
+ *---------------------------------------------------------------------------*/
+#define RTPRIO_NOTUSED (-1) /* rtprio is not used for isdnd */
+
+/*---------------------------------------------------------------------------*
+ * debug flag bits
+ *---------------------------------------------------------------------------*/
+#define DL_MSG 0x0001 /* general debug messages */
+#define DL_RATES 0x0002 /* messages related to rates */
+#define DL_TIME 0x0004 /* messages related to timing */
+#define DL_STATE 0x0008 /* messages related to states changes */
+#define DL_RCVRY 0x0010 /* messages related to dial recovery */
+#define DL_DIAL 0x0020 /* messages related to dial recovery */
+#define DL_PROC 0x0040 /* messages related to process handling */
+#define DL_DRVR 0x0080 /* messages related to kernel i4b msg i/o*/
+#define DL_CNST 0x0100 /* messages related to controller state */
+#define DL_RCCF 0x0200 /* messages related to isdnd.rc at boot */
+#define DL_BDGT 0x0400 /* messages related to budgets */
+#define DL_VALID 0x0800 /* messages related to valid keyword */
+
+#ifdef DEBUG
+#define DBGL(cond, dolog) if(cond & debug_flags) dolog
+#else
+#define DBGL(cond, dolog)
+#endif
+
+/*---------------------------------------------------------------------------*
+ * curses fullscreen display definitions
+ *---------------------------------------------------------------------------*/
+
+/* window dimensions */
+#define UPPER_B 2 /* upper window start */
+
+/* horizontal positions for upper window */
+#define H_CNTL 0 /* controller */
+#define H_TEI 2 /* TEI */
+#define H_CHAN (H_TEI+4) /* channel */
+#define H_TELN (H_CHAN+2) /* telephone number */
+#define H_IFN (H_TELN+23) /* interfacename */
+#define H_IO (H_IFN+7) /* incoming or outgoing */
+#define H_OUT (H_IO+4) /* # of bytes out */
+#define H_OUTBPS (H_OUT+11) /* bytes per second out */
+#define H_IN (H_OUTBPS+5) /* # of bytes in */
+#define H_INBPS (H_IN+11) /* bytes per second in */
+#define H_UNITS (H_INBPS+6) /* # of charging units */
+
+/* fullscreen mode menu window */
+#define WMENU_LEN 35 /* width of menu window */
+#define WMENU_TITLE "Command" /* title string */
+#define WMENU_POSLN 10 /* menu position, line */
+#define WMENU_POSCO 5 /* menu position, col */
+#define WMITEMS 6 /* no of menu items */
+#define WMENU_HGT (WMITEMS + 4) /* menu window height */
+
+#define WREFRESH 0
+#define WHANGUP 1
+#define WREREAD 2
+#define WSHOW 3
+#define WBUDGET 4
+#define WQUIT 5
+
+#define WMTIMEOUT 5 /* timeout in seconds */
+
+/*---------------------------------------------------------------------------*
+ * charging rates
+ *---------------------------------------------------------------------------*/
+#define NDAYS 7 /* number of days in a week */
+#define NRATES 4 /* number of rate structures supported */
+
+/* struct for rates - each day has one or more */
+struct rates
+{
+ int start_time; /* hour and min at which this rate starts, e.g. 12:20 12*60+20*/
+ int end_time; /* hour and min at which this rate ends, e.g. 19:10 19*60+10*/
+ int rate; /* how long can I telephone at this price, seconds */
+ struct rates *next;
+};
+
+/*---------------------------------------------------------------------------*
+ * the internal identifiers for isdnd log levels. CAUTION: this has to stay
+ * in sync with the loglevel to text and sysloglevel table in log.c !!
+ *---------------------------------------------------------------------------*/
+enum logids
+{
+ LL_ERR, /* error conditions - everything which caused an error */
+ LL_WRN, /* warning conditions - nonfatal abnormal conditions */
+ LL_DMN, /* normal but significant condition - status of daemon */
+ LL_CHD, /* informational - everything regarding call handling */
+ LL_DBG, /* debug messages - everything which helps debugging */
+ LL_MER, /* monitor error messages - not sent to remote */
+ LL_PKT /* packet logging - log the first few packets */
+};
+
+/*---------------------------------------------------------------------------*
+ * state machine events
+ *---------------------------------------------------------------------------*/
+enum events
+{
+ /* incoming messages */
+
+ EV_MCI, /* MSG_CONNECT_IND */
+ EV_MCAI, /* MSG_CONNECT_ACTIVE_IND */
+ EV_MDI, /* MSG_DISCONNECT_IND */
+ EV_MDO, /* MSG_DIALOUT */
+
+ /* local requests */
+
+ EV_TIMO, /* timer expired */
+ EV_DRQ, /* disconnect request */
+ EV_CBRQ, /* callback request */
+ EV_ALRT, /* alerting request */
+
+ /* illegal */
+
+ EV_ILL /* illegal event */
+};
+
+#define N_EVENTS (EV_ILL+1) /* no of possible events */
+
+/*---------------------------------------------------------------------------*
+ * this struct describes the numbers to try to dial out
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ char number[TELNO_MAX]; /* number */
+ char subaddr[SUBADDR_MAX]; /* subaddr */
+} number_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes the numbers to try to dial out
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ char number[TELNO_MAX]; /* remote number to dial */
+ char subaddr[SUBADDR_MAX]; /* remote subaddr */
+ int flag; /* usage flag */
+#define RNF_IDLE 0
+#define RNF_SUCC 1 /* last dial was ok */
+} remote_number_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes numbers allowed to dial in
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ char number[TELNO_MAX]; /* calling party number */
+ char subaddr[SUBADDR_MAX];/* calling party subaddr */
+} incoming_number_t;
+
+/*---------------------------------------------------------------------------*
+ * this structure describes a prematurely aborted called-back dialout
+ *---------------------------------------------------------------------------*/
+typedef struct {
+ int cdid; /* call handle */
+ int controller; /* the controller used to dial out */
+ int channel; /* the channel assigned to the outgoing call */
+ /* XXX - timeout handling and error recovery? */
+} phantom_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes one complete configuration entry
+ *---------------------------------------------------------------------------*/
+typedef struct cfg_entry {
+
+ /* ====== filled in at startup configuration, then static ===========*/
+
+ char name[32]; /* id for this entry */
+
+ int isdncontroller; /* controller to use 0 ... n */
+ int isdnchannel; /* channel to use */
+
+ int isdntxdelin; /* tx delay, incoming connections */
+ int isdntxdelout; /* tx delay, outgoing connections */
+
+ int usrdevicename; /* userland device to use */
+ int usrdeviceunit; /* userland unit to use */
+
+ int remote_numbers_count; /* number of remote numbers */
+ int remote_subaddr_count; /* number of remote subaddr */
+#define MAXRNUMBERS 8 /* max remote numbers */
+
+ remote_number_t remote_numbers[MAXRNUMBERS]; /* remote numbers to dial */
+
+
+ int remote_numbers_handling; /* how to handle the remote dialing */
+#define RNH_NEXT 0 /* use next number after last successfull */
+#define RNH_LAST 1 /* use last successfull for next call */
+#define RNH_FIRST 2 /* always use first number for next call */
+
+ number_t local_phone_dialout; /* our number to tell remote*/
+ number_t local_phone_incoming; /* answer calls for this local number */
+
+#define MAX_INCOMING 8
+ int incoming_numbers_count; /* number of incoming allowed numbers */
+ int incoming_subaddr_count; /* number of incoming allowed subaddr */
+ incoming_number_t remote_phone_incoming[MAX_INCOMING]; /* answer calls from this remote machine */
+
+ int dialin_reaction; /* what to do with incoming calls */
+#define REACT_ACCEPT 0
+#define REACT_REJECT 1
+#define REACT_IGNORE 2
+#define REACT_ANSWER 3
+#define REACT_CALLBACK 4
+
+ int b1protocol; /* hdlc / raw */
+
+ int bcap; /* special bearer capability (DoV) */
+
+ int idle_time_in; /* max idle time incoming calls */
+ int idle_time_out; /* max idle time outgoing calls */
+
+ int shorthold_algorithm; /* shorthold algorithm */
+
+ int unitlength; /* length of a charging unit */
+#define UNITLENGTH_DEFAULT 60 /* last resort unit length */
+
+ int earlyhangup; /* time in seconds to hangup */
+ /* before the next expected */
+ /* charging unit */
+#define EARLYHANGUP_DEFAULT 5
+
+ int ratetype; /* type of rate */
+#define NO_RATE (NRATES+1)
+#define INVALID_RATE (-1)
+
+ int unitlengthsrc; /* where we get the unit length from */
+#define ULSRC_NONE 0 /* nowhere specified */
+#define ULSRC_CMDL 1 /* specified on commandline */
+#define ULSRC_CMDLMIN 5 /* minimum value from cmdl */
+#define ULSRC_CMDLMAX 3600 /* minimum value from cmdl */
+#define ULSRC_CONF 2 /* get it from config file */
+#define ULSRC_RATE 3 /* get it dynamic from ratesfile*/
+#define ULSRC_DYN 4 /* dynamic calculated from AOCD */
+
+ char *answerprog; /* program to use for answering */
+ char *connectprog; /* program run after negotiation finished */
+ char *disconnectprog; /* program run after shutdown is complete */
+
+ int callbackwait; /* time to wait before calling back */
+#define CALLBACKWAIT_MIN 1
+
+ int calledbackwait; /* time to wait for remote callback */
+#define CALLEDBACKWAIT_MIN 2
+
+ int dialretries; /* no. of dial tries */
+#define DIALRETRIES_DEF 1
+
+ int recoverytime; /* time between 2 dial tries */
+#define RECOVERYTIME_MIN 1
+
+ int dialrandincr; /* use random dial time incr */
+
+ int usedown; /* set interface down yes/no */
+ int downtries; /* retries before i/f is set down */
+#define DOWN_TRIES_MIN 2
+#define DOWN_TRIES_MAX 20
+ int downtime; /* time i/f is down */
+#define DOWN_TIME_MIN 10 /* 10 seconds */
+#define DOWN_TIME_MAX 3600 /* 1 hour */
+
+ int dialouttype; /* type of outgoing connection */
+#define DIALOUT_NORMAL 0 /* normal dialout behaviour */
+#define DIALOUT_CALLEDBACK 1 /* remote is expected to callback */
+
+ int alert; /* alert time in sec if nonzero */
+#define MINALERT 5 /* 5 secs min */
+#define MAXALERT (3*60) /* 3 minutes max */
+
+ int inout; /* in/out, in-only or out-only */
+#define DIR_INOUT 0
+#define DIR_INONLY 1
+#define DIR_OUTONLY 2
+
+ int usesubaddr; /* use subaddresses */
+
+ int budget_callbackperiod; /* length of a budget period (s)*/
+ int budget_callbackncalls; /* call budget for a period */
+ char *budget_callbacks_file; /* filename to store callback stats */
+ char budget_callbacksfile_rotate;
+
+ int budget_calloutperiod; /* length of a budget period (s)*/
+ int budget_calloutncalls; /* call budget for a period */
+ char *budget_callouts_file; /* filename to store callout stats */
+ char budget_calloutsfile_rotate;
+
+ int ppp_expect_auth;
+ int ppp_send_auth;
+#define AUTH_UNDEF 0
+#define AUTH_NONE 1
+#define AUTH_PAP 2
+#define AUTH_CHAP 3
+
+ int ppp_auth_flags;
+#define AUTH_RECHALLENGE 0x01
+#define AUTH_REQUIRED 0x02
+
+ char ppp_expect_name[AUTHNAMELEN]; /* PPP PAP/CHAP login name */
+ char ppp_send_name[AUTHNAMELEN];
+
+ char ppp_expect_password[AUTHKEYLEN];
+ char ppp_send_password[AUTHKEYLEN];
+
+ int day; /* days valid */
+#define SU 0x01
+#define MO 0x02
+#define TU 0x04
+#define WE 0x08
+#define TH 0x10
+#define FR 0x20
+#define SA 0x40
+#define HD 0x80 /* holiday */
+ int fromhr; /* time valid */
+ int frommin;
+ int tohr;
+ int tomin;
+
+ time_t maxconnecttime; /* maximum connection time */
+
+/*===========================================================================*/
+/*============ filled in after start, then dynamic ==========================*/
+/*===========================================================================*/
+
+ int cdid; /* cdid for call */
+#define CDID_RESERVED (-1)
+
+ int isdncontrollerused; /* the one we are using */
+ int isdnchannelused; /* the one we are using */
+
+ int fs_position; /* fullscreen position */
+
+ int state; /* state of connection */
+#define ST_IDLE 0 /* connection is idle / disconnected */
+
+ /* normal dial out to remote */
+#define ST_DIAL 1 /* dialing */
+#define ST_DIALRTMRCHD 2 /* wait for dial retry time reached */
+#define ST_DIALRETRY 3 /* last/first dialing failed, retry */
+
+ /* PCB: passive callback, i'm being called back */
+#define ST_PCB_DIAL 4 /* dialing, trigger a callback */
+#define ST_PCB_DIALFAIL 5 /* dialing failed triggering a callbk */
+#define ST_PCB_WAITCALL 6 /* waiting for callback from remote */
+
+ /* ACB: active callback, i'm calling back */
+#define ST_ACB_WAITDISC 7 /* got call, wait for disconnect */
+#define ST_ACB_WAITDIAL 8 /* wait until allowed to callback */
+#define ST_ACB_DIAL 9 /* callback to remote */
+#define ST_ACB_DIALFAIL 10 /* callback to remote failed */
+
+ /* normal non-dialling states */
+#define ST_ACCEPTED 11 /* remote accepted */
+#define ST_CONNECTED 12 /* connected with remote */
+#define ST_WAITDISCI 13 /* tx disc req, wait for disc ind */
+#define ST_DOWN 14 /* interface is down */
+#define ST_ALERT 15 /* interface is waiting for alert time*/
+
+ /* illegal and pseudo states */
+#define ST_ILL 16 /* illegal state */
+#define ST_SUSE 17 /* subroutine sets new state */
+
+#define N_STATES (ST_ILL+1) /* max number of states */
+
+ cause_t disc_cause; /* cause from disconnect */
+
+ int local_disconnect; /* flag, who disconnected */
+#define DISCON_LOC 0
+#define DISCON_REM 1
+
+ int timerval; /* value for timer, 0 if inactive */
+ int timerremain; /* remaining time */
+
+ int hangup; /* flag, hangup connection asap */
+
+ number_t real_phone_incoming; /* real remote telno in case of wildcard */
+
+ int last_remote_number; /* index of last used dialout number*/
+
+ number_t remote_phone_dialout; /* used remote number to dial */
+
+ int direction; /* incoming or outgoing */
+#define DIR_IN 0
+#define DIR_OUT 1
+
+ int charge; /* charge in units */
+ int last_charge; /* last charge in units */
+
+ int inbytes; /* # of bytes from remote */
+ int iinbytes; /* # of bytes from remote on the line */
+ int inbps; /* bytes/sec from remote */
+ int outbytes; /* # of bytes to remote */
+ int ioutbytes; /* # of bytes to remote on the line */
+ int outbps; /* bytes/sec to remote */
+
+ time_t connect_time; /* time connection was established */
+
+ time_t aoc_last; /* last AOCD timestamp */
+ time_t aoc_now; /* current AOCD timestamp */
+ time_t aoc_diff; /* current unit length */
+ time_t aoc_lastdiff; /* last charge unit length */
+ int aoc_valid; /* flag: time diff is valid */
+#define AOC_INVALID 0 /* aoc_diff is NOT valid */
+#define AOC_VALID 1 /* aoc_diff is valid */
+
+ time_t last_dial_time; /* time of last dialing */
+ time_t last_release_time; /* time of last hangup */
+
+ int dial_count; /* number of dialout tries */
+ int randomtime; /* random() part of recoverytime*/
+#define RANDOM_MASK 0x04 /* bits used from randomtime */
+
+ int down_retry_count; /* retry cycle count for usedown*/
+ time_t went_down_time; /* time i/f went down */
+ phantom_t saved_call; /* outgoing call state if called
+ back too early */
+
+ int alert_time; /* count down of alert time */
+ char display[DISPLAY_MAX];
+
+ time_t budget_callbackperiod_time; /* end of current period */
+ int budget_callbackncalls_cnt; /* amount of calls left */
+
+ int budget_callback_req; /* requests */
+ int budget_callback_done; /* call done */
+ int budget_callback_rej; /* call refused */
+
+ time_t budget_calloutperiod_time; /* end of current period */
+ int budget_calloutncalls_cnt; /* amount of calls left */
+
+ int budget_callout_req; /* requests */
+ int budget_callout_done; /* call done */
+ int budget_callout_rej; /* call refused */
+
+ int budget_calltype; /* type of call */
+#define BUDGET_TYPE_CBACK 1
+#define BUDGET_TYPE_COUT 2
+
+ char keypad[KEYPAD_MAX]; /* keypad string */
+} cfg_entry_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes state of controller with MAX_BCHAN b channels
+ *---------------------------------------------------------------------------*/
+typedef struct isdn_ctrl_state {
+ int ctrl_type; /* type: active/passive */
+ int card_type; /* manufacturer (CARD_XXXX) */
+ int protocol; /* ISDN D-channel protocol */
+ char* firmware; /* loadable fimrware file name */
+
+ int state; /* controller state */
+#define CTRL_DOWN 0 /* controller inoparable */
+#define CTRL_UP 1 /* controller may be used */
+#define MAX_BCHAN 30
+ int stateb[MAX_BCHAN]; /* b channel state */
+#define CHAN_IDLE 0 /* channel is free for usage */
+#define CHAN_RUN 1 /* channel is occupied */
+ int nbch; /* number of b channels */
+ int freechans; /* number of unused channels */
+ int tei; /* tei or -1 if invalid */
+ int l1stat; /* layer 1 state */
+ int l2stat; /* layer 2 state */
+} isdn_ctrl_state_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes a logging regular expression
+ *---------------------------------------------------------------------------*/
+struct rarr {
+ int re_flg; /* valid entry flag */
+ char *re_expr; /* plain text expression */
+ regex_t re; /* compiled expression */
+ char *re_prog; /* the program to be executed */
+};
+
+#ifdef I4B_EXTERNAL_MONITOR
+/* for each rights entry we keep one of this structures around: */
+struct monitor_rights {
+ TAILQ_ENTRY(monitor_rights) list; /* a list of this structures */
+ char name[FILENAME_MAX]; /* net/host spec or filename */
+ int rights; /* bitmask of allowed acces rights */
+ u_int32_t net; /* net/host address (host byte order!) */
+ u_int32_t mask; /* bitmask 1 = network, 0 = host (host byte order!) */
+ int local; /* zero if remote access via tcp/ip */
+};
+#endif
+
+/*---------------------------------------------------------------------------*
+ * global variables, storage allocation
+ *---------------------------------------------------------------------------*/
+#ifdef MAIN
+
+int isdnfd; /* file handle, /dev/i4b */
+
+char mailto[MAXPATHLEN] = ""; /* panic mail address */
+char mailer[MAXPATHLEN] = ""; /* panic mail address */
+
+char *configfile = CONFIG_FILE_DEF; /* configuration filename */
+int config_error_flag = 0; /* error counter */
+
+#ifdef DEBUG
+int do_debug = 0; /* debug mode flag */
+int debug_flags = 0; /* debug options */
+int debug_noscreen = 0; /* not on fullscreen */
+#endif
+
+int do_bell = 0; /* bell on connect/disconnect */
+
+int do_fork = 1; /* run as daemon/foreground */
+
+int do_ttytype = 0; /* got new terminal type */
+char *ttype = ""; /* termcap entry name string */
+
+int do_rdev = 0; /* redirect output */
+char *rdev = ""; /* new device string */
+
+int do_print = 0; /* config file printout */
+
+int got_unitlen = 0; /* flag, got length of a unit */
+time_t unit_length; /* length of a unit */
+
+cfg_entry_t cfg_entry_tab[CFG_ENTRY_MAX]; /* configuration table */
+isdn_ctrl_state_t isdn_ctrl_tab[ISDN_CTRL_MAX]; /* controller states table */
+
+int ncontroller = 0; /* # of controllers available */
+int nentries = 0; /* # of entries in config tab */
+
+int uselogfile = 0; /* flag, use a logfile */
+char logfile[MAXPATHLEN] = LOG_FILE_DEF; /* log filename */
+FILE *logfp = NULL; /* log file pointer */
+int logfacility = LOG_LOCAL0; /* the syslog facility used */
+int nregex = 0; /* number of reg expr */
+struct rarr rarr[MAX_RE]; /* regexpr & progs table */
+
+char ratesfile[MAXPATHLEN] = RATES_FILE_DEF; /* rates filename */
+char *rate_error = NULL; /* errorcase: error string */
+int got_rate = 0; /* flag, ratesfile found */
+struct rates *rates[NRATES][NDAYS]; /* the rates structure */
+
+int useacctfile = 0; /* flag, write accounting */
+char acctfile[MAXPATHLEN] = ACCT_FILE_DEF; /* accounting filename */
+FILE *acctfp = NULL; /* accounting file pointer */
+int acct_all = 1; /* account all connections */
+
+int aliasing = 0; /* enable alias processing */
+char aliasfile[MAXPATHLEN] = ALIASFILE; /* alias file location */
+
+int do_fullscreen = 0; /* fullscreen log */
+int curses_ready = 0; /* curses initialized */
+
+#ifdef USE_CURSES
+WINDOW *upper_w; /* curses upper window pointer */
+WINDOW *mid_w; /* curses mid window pointer */
+WINDOW *lower_w; /* curses lower window pointer */
+#endif
+
+int rt_prio = RTPRIO_NOTUSED; /* realtime priority */
+
+/* monitor via network */
+
+int do_monitor = 0;
+int inhibit_monitor = 0;
+#ifdef I4B_EXTERNAL_MONITOR
+int monitorport = DEF_MONPORT;
+#else
+int monitorport = -1;
+#endif
+int accepted = 0;
+
+int isdntime = 0; /* flag, log time from exchange */
+int extcallattr = 0; /* flag, display extended caller attributes */
+
+char tinainitprog[MAXPATHLEN] = TINA_FILE_DEF;
+
+char rotatesuffix[MAXPATHLEN] = "";
+
+time_t starttime = 0;
+
+char holidayfile[MAXPATHLEN] = HOLIDAY_FILE_DEF; /* holiday filename */
+
+int addprefix = 0;
+char prefixnational[TELNO_MAX] = "";
+char prefixinternational[TELNO_MAX] = "";
+
+#else /* !MAIN */
+
+int isdnfd;
+
+char mailto[MAXPATHLEN];
+char mailer[MAXPATHLEN];
+
+char *configfile;
+int config_error_flag;
+
+#ifdef DEBUG
+int do_debug;
+int debug_flags;
+int debug_noscreen;
+#endif
+
+int do_bell;
+
+int do_fork;
+
+int do_ttytype;
+char *ttype;
+
+int do_rdev;
+char *rdev;
+
+int do_print;
+
+int got_unitlen;
+time_t unit_length;
+
+cfg_entry_t cfg_entry_tab[CFG_ENTRY_MAX]; /* configuration table */
+isdn_ctrl_state_t isdn_ctrl_tab[ISDN_CTRL_MAX]; /* controller states table */
+
+int ncontroller;
+int nentries;
+
+int uselogfile;
+char logfile[MAXPATHLEN];
+FILE *logfp;
+int logfacility;
+int nregex;
+struct rarr rarr[MAX_RE];
+
+char ratesfile[MAXPATHLEN];
+char *rate_error;
+int got_rate;
+struct rates *rates[NRATES][NDAYS];
+
+int useacctfile;
+char acctfile[MAXPATHLEN];
+FILE *acctfp;
+int acct_all;
+
+int aliasing;
+char aliasfile[MAXPATHLEN];
+
+int do_fullscreen;
+int curses_ready;
+
+#ifdef USE_CURSES
+WINDOW *upper_w;
+WINDOW *mid_w;
+WINDOW *lower_w;
+#endif
+
+int rt_prio;
+
+int do_monitor;
+int inhibit_monitor;
+int monitorport;
+int accepted;
+
+int isdntime;
+int extcallattr;
+
+char tinainitprog[MAXPATHLEN];
+
+char rotatesuffix[MAXPATHLEN];
+
+time_t starttime;
+
+char holidayfile[MAXPATHLEN];
+
+int addprefix;
+char prefixnational[TELNO_MAX];
+char prefixinternational[TELNO_MAX];
+
+#endif /* MAIN */
+
+char * bdrivername ( int drivertype );
+void cfg_setval ( int keyword );
+void check_and_kill ( cfg_entry_t *cep );
+void check_pid ( void );
+void close_allactive ( void );
+void configure ( char *filename, int reread );
+void daemonize ( void );
+void dialresponse(cfg_entry_t *cep, int dstat);
+void display_acct ( cfg_entry_t *cep );
+void display_bell ( void );
+void display_ccharge ( cfg_entry_t *cep, int units );
+void display_chans ( void );
+void display_charge ( cfg_entry_t *cep );
+void display_connect ( cfg_entry_t *cep );
+void display_disconnect ( cfg_entry_t *cep );
+void display_l12stat(int controller, int layer, int state);
+void display_tei(int controller, int tei);
+void display_updown ( cfg_entry_t *cep, int updown );
+void do_exit ( int exitval );
+void do_menu ( void );
+int exec_answer ( cfg_entry_t *cep );
+int exec_connect_prog ( cfg_entry_t *cep, const char *prog, int link_down );
+pid_t exec_prog ( char *prog, char **arglist );
+cfg_entry_t * find_by_device_for_dialout ( int drivertype, int driverunit );
+cfg_entry_t *find_by_device_for_dialoutnumber(msg_dialoutnumber_ind_t *mp);
+cfg_entry_t *find_by_device_for_keypad(int drivertype, int driverunit, int cmdlen, char *cmd);
+cfg_entry_t * find_matching_entry_incoming ( msg_connect_ind_t *mp );
+cfg_entry_t * find_active_entry_by_driver ( int drivertype, int driverunit );
+void finish_log ( void );
+char * getlogdatetime ( void );
+int get_cdid ( void );
+cfg_entry_t * get_cep_by_cc ( int ctrlr, int chan );
+cfg_entry_t * get_cep_by_driver ( int drivertype, int driverunit );
+cfg_entry_t * get_cep_by_cdid ( int cdid );
+int get_current_rate ( cfg_entry_t *cep, int logit );
+void handle_charge ( cfg_entry_t *cep );
+void handle_recovery ( void );
+void handle_scrprs(int cdid, int scr, int prs, char *caller);
+void if_up(cfg_entry_t *cep);
+void if_down(cfg_entry_t *cep);
+void init_controller ( void );
+void init_controller_protocol ( void );
+void init_log ( void );
+void init_screen ( void );
+void log ( int what, const char *fmt, ... );
+int main ( int argc, char **argv );
+void msg_accounting ( msg_accounting_ind_t *mp );
+void msg_alert_ind ( msg_alert_ind_t *mp );
+void msg_charging_ind ( msg_charging_ind_t *mp );
+void msg_connect_active_ind ( msg_connect_active_ind_t *mp );
+void msg_connect_ind ( msg_connect_ind_t *mp );
+void msg_pdeact_ind(msg_pdeact_ind_t *md);
+void msg_negcomplete_ind(msg_negcomplete_ind_t *ind);
+void msg_ifstatechg_ind(msg_ifstatechg_ind_t *ind);
+void msg_drvrdisc_req(msg_drvrdisc_req_t *mp);
+void msg_dialout ( msg_dialout_ind_t *mp );
+void msg_dialoutnumber(msg_dialoutnumber_ind_t *mp);
+void msg_disconnect_ind ( msg_disconnect_ind_t *mp );
+void msg_idle_timeout_ind ( msg_idle_timeout_ind_t *mp );
+void msg_l12stat_ind(msg_l12stat_ind_t *ml);
+void msg_teiasg_ind(msg_teiasg_ind_t *mt);
+void msg_proceeding_ind ( msg_proceeding_ind_t *mp );
+void msg_packet_ind( msg_packet_ind_t *mp );
+const char * name_of_controller(int ctrl_type, int card_type);
+void next_state ( cfg_entry_t *cep, int event );
+char * print_i4b_cause( cause_t code );
+char * printstate ( cfg_entry_t *cep );
+int readrates ( char *filename );
+int ret_channel_state(int controller, int channel);
+void reopenfiles ( int dummy );
+void rereadconfig ( int dummy );
+void select_first_dialno ( cfg_entry_t *cep );
+void select_next_dialno ( cfg_entry_t *cep );
+void select_this_dialno ( cfg_entry_t *cep );
+int sendm_alert_req ( cfg_entry_t *cep );
+int sendm_connect_req ( cfg_entry_t *cep );
+int sendm_connect_resp ( cfg_entry_t *cep, int cdid, int response, cause_t cause );
+int sendm_disconnect_req ( cfg_entry_t *cep, cause_t cause );
+int set_channel_busy(int controller, int channel);
+int set_channel_idle(int controller, int channel);
+int setup_dialout(cfg_entry_t *cep);
+void sigchild_handler ( int sig );
+void start_timer ( cfg_entry_t *cep, int seconds );
+void stop_timer ( cfg_entry_t *cep );
+void unitlen_chkupd( cfg_entry_t *cep );
+void write_pid ( void );
+void yyerror ( const char *msg );
+
+void error_exit(int exitval, const char *fmt, ...);
+
+/* montior server module */
+void monitor_init();
+void monitor_exit();
+void monitor_clear_rights();
+void monitor_fixup_rights();
+int monitor_start_rights(const char *clientspec);
+void monitor_add_rights(int rights);
+
+/* possible return codes from monitor_start_rights: */
+#define I4BMAR_OK 0 /* rights added successfully */
+#define I4BMAR_LENGTH 1 /* local socket name to long */
+#define I4BMAR_DUP 2 /* entry already exists */
+#define I4BMAR_CIDR 3 /* cidr netmask is invalid */
+#define I4BMAR_NOIP 4 /* host/net could not be resolved */
+
+int monitor_create_local_socket();
+
+#ifndef I4B_NOTCPIP_MONITOR
+int monitor_create_remote_socket(int portno);
+#endif
+
+void monitor_prepselect(fd_set *selset, int *max_fd);
+void monitor_handle_input(fd_set *selset);
+void monitor_handle_connect(int sockfd, int is_local);
+void monitor_evnt_charge(cfg_entry_t *cep, int units, int estimated);
+void monitor_evnt_connect(cfg_entry_t *cep);
+void monitor_evnt_disconnect(cfg_entry_t *cep);
+void monitor_evnt_updown(cfg_entry_t *cep, int up);
+void monitor_evnt_log(int prio, const char * what, const char * msg);
+
+void monitor_evnt_l12stat(int controller, int layer, int state);
+void monitor_evnt_tei(int controller, int tei);
+void monitor_evnt_acct(cfg_entry_t *cep);
+
+/* controller.c */
+
+void init_active_controller(void);
+int set_controller_state(int controller, int state);
+int get_controller_state(int controller);
+int decr_free_channels(int controller);
+int incr_free_channels(int controller);
+int get_free_channels(int controller);
+int set_channel_busy(int controller, int channel);
+int set_channel_idle(int controller, int channel);
+int ret_channel_state(int controller, int channel);
+
+/* alias.c */
+
+void init_alias(char *filename);
+void free_aliases(void);
+char *get_alias(char *number);
+
+void upd_callstat_file(char *filename, int rotateflag);
+
+/* holiday.c */
+
+void init_holidays(char *filename);
+void free_holidays(void);
+int isholiday(int d, int m, int y);
+
+#endif /* _ISDND_H_ */
diff --git a/usr.sbin/i4b/isdnd/isdnd.rates.5 b/usr.sbin/i4b/isdnd/isdnd.rates.5
new file mode 100644
index 0000000..7ada783
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.rates.5
@@ -0,0 +1,113 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdnd.rates.5,v 1.10 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 22:59:31 1999]
+.\"
+.Dd September 11, 1998
+.Dt ISDND.RATES 5
+.Os
+.Sh NAME
+.Nm isdnd.rates
+.Nd isdn4bsd ISDN management daemon rates description file
+.Sh DESCRIPTION
+The file
+.Pa isdnd.rates
+contains descriptions how long charging units last at a given time of day,
+day of week and the distance to the destination. If this file is available,
+this information may be used by the
+.Xr isdnd 8
+ISDN connection management daemon to calculate the short hold time for a
+connection.
+.Pp
+The format of a rate entry line is as follows:
+.Pp
+The first field, the
+.Pq Fa rate-code
+defines a collection of rates (for each day of the week) which can be
+referenced in the
+.Xr isdnd 8
+configuration file
+.Xr isdnd.rc 5 .
+This field must start with the identifier
+.Dq ra
+followed by a digit in the range of zero to four.
+.Pp
+The second field, the
+.Pq Fa day-number
+selects the day of week for which this entry defines the rates, where 0 stands
+for Sunday, 1 for Monday and so on until the digit 6 which stands for Saturday.
+.Pp
+The rest of the line consists of one or more space separated fields which have
+the following syntax:
+.Bd -ragged -offset indent
+start_hour.start_minutes-end_hour.end_minutes:charge_unit_length
+.Ed
+.Pp
+Start_hour and start_minutes define the begin of a time section and end_hour
+and end_minutes define the end. Charge_unit_length define the length of a
+charging unit in the previously defined time section. No spaces or tabs are
+allowed inside this field. The hour and minutes specifications MUST have
+exactly 2 digits, in case just one digit is needed, a leading 0 must be used.
+.Pp
+For example,
+.Bd -ragged -offset indent
+14.00-18.00:90
+.Ed
+.Pp
+defines, that between 2:00 PM and 6:00 PM the length of one charging unit
+lasts 90 seconds.
+.Sh FILES
+.Bl -tag -width /etc/isdn/isdnd.rates -compact
+.It Pa /etc/isdn/isdnd.rates
+The default rates specification file for the
+.Nm isdnd
+ISDN daemon.
+.El
+.Sh EXAMPLES
+The line:
+.Bd -literal
+ra0 0 00.00-05.00:240 05.00-21.00:150 21.00-24.00:240
+.Ed
+.Pp
+defines the unit lengths for a Sunday.
+.Sh SEE ALSO
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+.An -nosplit
+The rates subsystem for the
+.Xr isdnd 8
+daemon to which
+.Nm
+belongs was designed and written by
+.An Gary Jennejohn .
+.Pp
+The
+.Xr isdnd 8
+daemon and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdnd/isdnd.rc.5 b/usr.sbin/i4b/isdnd/isdnd.rc.5
new file mode 100644
index 0000000..8fde2fd
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.rc.5
@@ -0,0 +1,1115 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Aug 11 20:07:38 2002]
+.\"
+.Dd August 11, 2002
+.Dt ISDND.RC 5
+.Os
+.Sh NAME
+.Nm isdnd.rc
+.Nd isdn4bsd ISDN connection management daemon config file format
+.Sh DESCRIPTION
+The file
+.Pa /etc/isdn/isdnd.rc
+contains (if not otherwise specified on the command line) the runtime
+configuration for the
+.Xr isdnd 8
+ISDN connection management daemon which is part of the isdn4bsd package.
+.Pp
+The configuration file consists of keywords which start in column 1 followed by
+one or more spaces or tabs, an equal sign, one or more spaces or tabs
+and a keyword dependent parameter value.
+.Pp
+A line beginning with '#' is treated as a comment line.
+.Pp
+For keywords requiring the specification of a boolean value, the truth
+value can be either
+.Em yes
+or
+.Em on
+while the false value can be either
+.Em no
+or
+.Em off .
+.Pp
+The configuration file consists of one
+.Em system
+section, one or more optional
+.Em controller
+sections and one or more
+.Em entry
+sections.
+In the
+.Em system
+section parameters regarding the daemon operation or parameters
+not associated with a single remote connection can be set.
+In the
+.Em controller
+section parameters regarding a particular controller can be set.
+In the
+.Em entry
+section(s) parameters directly associated with a single remote
+connection can be set.
+.Pp
+The following keywords are recognized by
+.Nm isdnd :
+.Pp
+.Bl -tag -width system
+.It Li system
+This keyword starts the system configuration section.
+It must not
+have a parameter and may be used only once.
+The keyword is mandatory.
+The following keywords are valid in the system configuration section:
+.Bl -tag -width useacctfile
+.It Li acctall
+If this parameter is set to
+.Em on ,
+accounting information is written even if the local site was not charged
+or no charging information is available or is not subscribed.
+(optional)
+.It Li acctfile
+Specifies the name of the accounting file which is used when the keyword
+.Em useacctfile
+(see below) is set to
+.Em on .
+See also system keyword
+.Em rotatesuffix .
+If this keyword is omitted the system default is used.
+(optional)
+.It Li add-prefix
+If set to
+.Em on ,
+for incoming numbers, have a look at the "type of number" indicator and
+adjust the number as specified by the
+.Em prefix-national
+and
+.Em prefix-international
+keywords described later.
+If this keyword is omitted, the system default (off) is used.
+(optional)
+.It Li aliasing
+If this parameter is set to
+.Em on ,
+alias processing of telephone-number to name is enabled (see also the
+.Em aliasfile
+keyword below).
+The default is off.
+(optional)
+.It Li aliasfile
+Specifies the name of the telephone number-to-name alias database file shared
+with the
+.Xr isdntel 1
+utility when alias processing is enabled via the
+.Em aliasing
+keyword.
+(optional)
+.It Li beepconnect
+In full-screen mode, if this parameter is set to
+.Em on ,
+ring the bell when connecting or disconnecting a call.
+.It Li extcallattr
+If this parameter is set to
+.Em on ,
+the extended caller attributes "screening indicator" and "presentation
+indicator" are written to the log-file.
+The default is off.
+(optional)
+.It Li holidayfile
+Specifies the name of the holiday file containing the dates of holidays.
+This file is used in conjunction with the
+.Em valid
+keyword to lookup the dates of holidays.
+(optional)
+.It Li isdntime
+If this parameter is set to
+.Em on ,
+date/time information from the exchange (if provided) is written to the
+log-file.
+The default is off.
+(optional)
+.It Li mailer
+This keyword is used to specify the path/name of a mail program
+which is able to use the "-s" flag to specify a subject on its
+command line.
+In case of a fatal error exit of
+.Nm
+this program is used to send mail to an administrator specified by
+the keyword
+.Em mailto .
+(optional)
+.It Li mailto
+This keyword is used to specify the email address of someone to notify
+in case of a fatal error exit of
+.Nm .
+(See also keyword
+.Em mailer ) .
+(optional)
+.It Li monitor-allowed
+If this parameter is set to
+.Em on
+or
+.Em yes ,
+monitoring via a local or remote machine is enabled.
+This parameter is optional and is set to
+.Em off
+by default.
+.It Li monitor-port
+sets the TCP port number for remote monitoring.
+This integer parameter is optional and is set to port 451 by default.
+.It Li monitor
+This keyword specifies a local socket name or a host or network for remote
+monitoring.
+The
+.Em monitor
+specification may either be:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar the name of a local (UNIX-domain) socket
+this MUST start with a "/", example: /var/run/isdn-monitor
+.It Ar a dotted-quad host specification
+example: 192.168.1.2
+.It Ar a dotted-quad network address with netmask
+example: 192.168.1.0/24
+.It Ar a resolvable host name
+example: localhost
+.It Ar a resolvable network name with netmask
+example: up-vision-net/24
+.El
+.It Li monitor-access
+This keyword specifies the access rights for a previously used
+.Em monitor
+keyword.
+The supported access rights are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar fullcmd
+.It Ar restrictedcmd
+.It Ar channelstate
+.It Ar logevents
+.It Ar callin
+.It Ar callout
+.El
+.It Li prefix-international
+In conjunction with the
+.Em add-prefix
+switch (see above), specify the prefix number string for incoming numbers
+with the international number tag.
+If aliases are used, they have to be adjusted accordingly.
+(optional)
+.It Li prefix-national
+In conjunction with the
+.Em add-prefix
+switch (see above), specify the prefix number string for incoming numbers
+with the national number tag.
+If aliases are used, they have to be adjusted accordingly.
+(optional)
+.It Li ratesfile
+Specifies the name of the ratesfile.
+If this keyword is omitted the system
+default is used.
+(optional)
+.It Li regexpr
+This keyword is used to specify regular expressions.
+It can be specified
+more than once up to a compile time dependent value (currently set to 5 by
+the MAX_RE definition in the source).
+.Pp
+All specified regular expressions are compared to the log strings at runtime
+and if a match is found, a program is run with the log text as a parameter
+(see also the keyword
+.Em regprog
+below).
+.Pp
+For an explanation how regular expressions are specified, please have a
+look at
+.Xr re_format 7
+and
+.Xr regex 3 .
+The
+.Em extended
+regular expression syntax is supported here.
+.Pp
+Hint: it might be necessary to properly quote the expression to avoid
+improper interpretation by the configuration file parser.
+(optional)
+.It Li regprog
+This keyword is used to specify the name of a program which is run in
+case a corresponding regular expression is matched by a logging string.
+.Nm Isdnd
+expects to find the program below the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+(optional)
+.It Li rotatesuffix
+Specifies a suffix for renaming the log- and the accounting-filename.
+In case
+rotatesuffix is used and a USR1 signal is sent to isdnd, the log-file and the
+accounting file is not only closed and reopened but the old log-file is also
+renamed to the former filename with the rotatesuffix string appended.
+If this keyword is omitted, the log-files are just closed and reopened; this
+is also the default behavior.
+(optional)
+.It Li rtprio
+Specifies the real-time priority
+.Nm isdnd
+runs at as an integer value in the range 0...31 with 0 being the highest
+priority.
+This keyword is optional; if not specified the process priority of
+.Nm isdnd
+is not touched in any way.
+( See also
+.Xr rtprio 1 ) .
+This keyword is only available if
+.Nm
+was compiled with -DUSE_RTPRIO.
+.It Li useacctfile
+If this parameter is set to
+.Em on
+charging (if available) and accounting information is written to the
+accounting file.
+(optional)
+.El
+.It Li controller
+This keyword starts the controller configuration section.
+It must not
+have a parameter and may be used once for every controller.
+The keyword
+is optional.
+The following keywords are valid in a controller
+configuration section:
+.Bl -tag -width useacctfile
+.It Li protocol
+This keyword is used to set the D-channel protocol for the S0-bus a
+controller is connected to.
+The following parameters are currently
+supported:
+.Pp
+.Bl -tag -width calledback -compact
+.It Ar dss1
+The DSS1 or so-called "Euro-ISDN" D-channel protocol according to
+ITU Recommendations Q.921 and Q.931.
+.It Ar d64s
+An ISDN leased line with a single B-channel (called D64S in Germany).
+.El
+.It Li firmware
+This keyword is used like
+.Li firmware Ns = Ns Ar /path/to/file
+to download the
+firmware to active controllers supported by the
+.Em iavc
+driver (AVM B1, T1).
+This keyword is supported for all controller types,
+and causes
+.Dv I4B_CTRL_DOWNLOAD
+ioctl to be invoked with the specified file
+as an argument.
+In systems equipped with both active and passive adapters,
+and the passive cards being detected first, dummy
+.Ql controller
+entries
+are required for the passive cards to get the correct firmwares to
+correct adapters.
+.El
+.It Li entry
+This keyword starts one configuration entry.
+It must not have a parameter.
+This keyword must be used at least once.
+The following keywords are valid in an entry section:
+.Bl -tag -width unitlengthsrc
+.It Li answerprog
+This keyword is used to specify the name of a program which is run in
+case an incoming telephone connection specified
+.Em answer
+in its configuration entry.
+The default name is
+.Em answer .
+.Nm Isdnd
+expects to find this program beneath the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+(optional)
+.It Li alert
+is used to specify a time in seconds to wait before accepting a call.
+This
+keyword is only usable for incoming telephone calls (dialin-reaction = answer).
+It is used to have a chance to accept an incoming call on the phone before
+the answering machine starts to run.
+The minimum value for the alert parameter
+is 5 seconds and the maximum parameter allowed is 180 seconds.
+(optional)
+.It Li b1protocol
+The B channel layer 1 protocol used for this connection.
+The keyword is mandatory.
+The currently configurable values are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar hdlc
+HDLC framing.
+.It Ar raw
+No framing at all (used for telephony).
+.El
+.It Li bcap
+Use a special bearer capability for this connection.
+The keyword is optional.
+.Pp
+Any other value than
+.Em dov
+sets the bearer capability as configured by the
+.Em b1protocol
+keyword (see above).
+The currently configurable values are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar dov
+This connection is a
+.Em Dov (Data over Voice)
+connection.
+The b1protocol keyword must be set to
+.Em hdlc .
+This feature is experimental and does work on outgoing calls only.
+.El
+.It Li budget-calloutperiod
+is used to specify a time period in seconds.
+Within this period, the number of calls
+specified by
+.Em budget-calloutncalls
+are allowed to succeed, any further attempt to call out will be blocked for the rest
+of the time left in the time period.
+(optional)
+.It Li budget-calloutncalls
+The number of outgoing calls allowed within the time period specified by
+.Em budget-calloutperiod .
+(optional)
+.It Li budget-calloutsfile
+A path/filename to which the number of successfull callouts are written.
+The contents of the file is preserved when it exists during startup of isdnd.
+The format of this file is: start time, last update time, number of calls.
+(optional)
+.It Li budget-calloutsfile-rotate
+If set to
+.Em on
+rotate budget-calloutsfile every night when an attempt is made to update
+the file on a new day.
+The statistics for the previous day are witten to
+a file with the filename specified by budget-calloutsfile to which a hyphen
+and the new day's (!) day of month number is appended.
+(optional)
+.It Li budget-callbackperiod
+.It Li budget-callbackncalls
+.It Li budget-callbacksfile
+.It Li budget-calloutsfile-rotate
+See
+.Em budget-calloutperiod ,
+.Em budget-calloutncalls ,
+.Em budget-calloutsfile ,
+and
+.Em budget-calloutsfile-rotate
+above.
+These are used to specify the budgets for calling back a remote site.
+.It Li callbackwait
+The time in seconds to wait between hanging up the call from a remote site
+and calling back the remote site.
+(optional)
+.It Li calledbackwait
+The time in seconds to wait for a remote site calling back the local site
+after a call from the local site to the remote site has been made.
+(optional)
+.It Li clone
+This causes the contents of the specified entry to be copied from the
+existing named entry to the current one.
+When using this feature at least a new entry specific
+.Ql name
+and
+.Ql usrdeviceunit
+value should be specified for the current entry.
+.It Li connectprog
+specifies a program run every time after a connection is established and
+address negotiation is complete (i.e.: the connection is usable).
+.Nm Isdnd
+expects to find the program below the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+The programs specified by connect and disconnect will get the following
+command line arguments: -d (device) -f (flag) [ -a (addr) ] where
+.Em device
+is the name of device, e.g. "isp0",
+.Em flag
+will be "up" if connection just got up, or "down" if interface changed to down
+state and
+.Em addr
+the address that got assigned to the interface as a dotted-quad ip address
+(optional, only if it can be figured out by isdnd).
+(optional)
+.It Li dialin-reaction
+Used to specify what to do when an incoming connection request is received.
+The keyword is mandatory.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width calledback -compact
+.It Ar accept
+Accept an incoming call.
+.It Ar reject
+Reject an incoming call.
+.It Ar ignore
+Ignore an incoming call.
+.It Ar answer
+Start telephone answering for an incoming voice call.
+.It Ar callback
+When a remote site calls, hang up and call back the remote site.
+.El
+.It Li dialout-type
+This keyword is used to configure what type of dialout mode is used.
+The keyword is mandatory.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar normal
+Normal behavior, call the remote site which is supposed to accept the call.
+.It Ar calledback
+Callback behavior, call the remote side which rejects the call and calls
+us back.
+.El
+.It Li dialrandincr
+When dialing or re-dialing and this parameter is set to
+.Em on ,
+the dial retry time is added with a random value (currently 0...3 seconds)
+to minimize the chance of two sites dialing synchronously so each gets a busy
+each time it dials because the other side is also dialing.
+.It Li dialretries
+The number of dialing retries before giving up.
+Setting this to
+.Em -1
+gives an unlimited number of retries!
+(optional)
+.It Li direction
+This keyword is used to configure if incoming and outgoing, incoming-only or
+outgoing only connections are possible.
+The keyword is optional, the default is
+.Em inout .
+.Pp
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar inout
+Normal behavior, connection establishment is possible from remote and local.
+.It Ar in
+Only incoming connections are possible.
+.It Ar out
+Only outgoing connections are possible.
+.El
+.It Li disconnectprog
+specifies a program run every time after a connection was shut down.
+.Nm Isdnd
+expects to find the program below the path
+.Pa /etc/isdn
+which is prepended to the string specified as a parameter to this keyword.
+(optional)
+.It Li downtries
+is used to configure the number of unsuccessful tries (= retry cycles!) before
+the interface is disabled (for
+.Em downtime
+seconds).
+(see also the keyword
+.Em usedown
+further up).
+This keyword is optional.
+.It Li downtime
+is used to configure the time in seconds an interface is disabled
+after the configured number of
+.Em downtries .
+(see also the keyword
+.Em usedown
+further up).
+This keyword is optional and is set to 60 seconds by default.
+.It Li earlyhangup
+A (safety) time in seconds which specifies the time to hang up before an
+expected next charging unit will occur.
+(optional)
+.It Li idle-algorithm-outgoing
+The algorithm used to determine when to hang up an outgoing call when the
+line becomes idle.
+The current algorithms are:
+.Pp
+.Bl -tag -width calledback -compact
+.It Ar fix-unit-size
+idle algorithm which assumes fixed sized charging units during the whole call.
+.It Ar var-unit-size
+idle algorithm which assumes that the charging is time based after the first
+units time has expired.
+.El
+.It Li idletime-outgoing
+The time in seconds an outgoing connection must be idle before hanging up.
+An idle timeout of zero disables this functionality.
+(optional)
+.It Li idletime-incoming
+The time in seconds an incoming connection must be idle before hanging up.
+An idle timeout of zero disables this functionality.
+(optional)
+.It Li isdncontroller
+The ISDN controller number to be used for connections for this entry.
+(mandatory)
+.It Li isdnchannel
+The ISDN controller channel number to be used for connections for this entry.
+In case a channel is explicitly selected here, the SETUP message will request
+this channel but mark the request as
+.Em preferred
+(the indicated channel is preferred) instead of exclusive (only the indicated
+channel is acceptable).
+Thus the exchange is still free to select another
+than the requested channel!
+(mandatory)
+.It Li isdntxdel-incoming
+A delay value suitable for the
+.Xr timeout 9
+kernel subroutine to delay the transmission of the first packet after a
+successful connection is made by this value for
+.Em incoming
+ISDN connections.
+The specification unit is 1/100 second.
+A zero (0) disables
+this feature and is the default value.
+This feature is implemented (and makes
+sense only) for the
+.Xr i4bipr 4
+IP over raw HDLC ISDN driver.
+(optional)
+.It Li isdntxdel-outgoing
+A delay value suitable for the
+.Xr timeout 9
+kernel subroutine to delay the transmission of the first packet after a
+successful connection is made by this value for
+.Em outgoing
+ISDN connections.
+The specification unit is 1/100 second.
+A zero (0) disables
+this feature and is the default value.
+This feature is implemented (and makes
+sense only) for the
+.Xr i4bipr 4
+IP over raw HDLC ISDN driver.
+(optional)
+.It Li local-phone-dialout
+The local telephone number used when the local site dials out.
+When dialing
+out to a remote site, the number specified here is put into the
+.Em "Calling Party Number Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+user-land interfaces.
+.It Li local-subaddr-dialout
+The local subaddress used when the local site dials out.
+When dialing
+out to a remote site, the subaddress specified here is put into the
+.Em "Calling Party Subaddress Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+user-land interfaces.
+.It Li local-phone-incoming
+The local telephone number used for verifying the destination of incoming
+calls.
+When a remote site dials in, this number is used to verify that it
+is the local site which the remote site wants to connect to.
+It is compared
+with the
+.Em "Called Party Number Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+.It Li local-subaddr-incoming
+The local subaddress used for verifying the destination of incoming
+calls.
+When a remote site dials in, this subaddress is used to verify that it
+is the local site which the remote site wants to connect to.
+It is compared
+with the
+.Em "Called Party Subaddress Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+.It Li name
+Defines a symbolic name for this configuration entry.
+Its purpose is to
+use this name in the full-screen display for easy identification of a link
+to a remote site and for accounting purposes.
+(mandatory)
+.It Li maxconnecttime
+Specify a maximum connection time in seconds.
+Use this to define an absolute
+upper limit for a connection on the B-channel to last.
+.Em CAUTION:
+This feature is used to limit the connection time, _not_ number of attempts
+to establish a connection: when using this please take care to also enable
+the use of budgets to limit the connection establish attempts (otherwise
+the line will cycle thru an endless loop of connections and reconnections
+which will have an undesired effect on your telco bill)!
+.It Li ppp-auth-paranoid
+If set to
+.Em no ,
+the remote site is not required to prove its authentity for connections
+that are initiated by the local site.
+The default is
+.Em yes
+and requires the remote site to always authenticate.
+.Pp
+This keyword is only used if
+.Em ppp-send-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-auth-rechallenge
+Set to
+.Em no ,
+if the other side does not support re-challenging for chap.
+The default is
+.Em yes ,
+which causes verification of the remote site's authentity once in a while.
+.Pp
+This keyword is only used if
+.Em ppp-expect-auth
+has been set to chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-expect-auth
+The local site expects the authentity of the remote site to be proved by
+the specified method.
+The supported methods are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar none
+Do not require the other side to authenticate.
+Typical uses are dial-out to an ISP
+(many ISPs do not authenticate themselves to clients)
+or offering anonymous dial-in at the local site.
+.It Ar chap
+The preferred authentication method, which does not require a password to be sent
+in the clear.
+.It Ar pap
+The unprotected authentication method, which allows anybody watching the wire
+to grab name and password.
+.El
+.Pp
+If
+.Em ppp-auth-paranoid
+is set to
+.Em no
+(the default is
+.Em yes )
+outgoing connections will not require the remote site to authenticate itself.
+.Pp
+This keyword is only used for the
+.Em isp
+PPP interfaces.
+(optional)
+.It Li ppp-expect-name
+The name that has to be provided by the remote site to prove its authentity.
+.Pp
+This keyword is only used if
+.Em ppp-expect-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-expect-password
+The secret that has to be provided by the remote site to prove its authentity.
+.Pp
+This keyword is only used if
+.Em ppp-expect-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-send-auth
+The authentication method required by the remote site.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar none
+The remote site does not expect or support authentication.
+.It Ar chap
+The preferred authentication method, which does not require a password to be sent
+in the clear.
+.It Ar pap
+The unprotected authentication method, which allows anybody watching the wire
+to grab name and password.
+.El
+.Pp
+This keyword is only used for the
+.Em isp
+PPP interfaces.
+(optional)
+.It Li ppp-send-name
+The authentication name sent to the remote site.
+.Pp
+This keyword is only used if
+.Em ppp-send-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ppp-send-password
+The secret used to prove the local site's authentity to the remote site.
+.Pp
+This keyword is only used if
+.Em ppp-send-auth
+has been set to pap or chap for an
+.Em isp
+PPP interface.
+(optional)
+.It Li ratetype
+The rate entry used from the rates file.
+(optional)
+.Pp
+For example, ratetype=0 selects lines beginning "ra0" in /etc/isdn/isdnd.rates;
+(typically ra0 lines are a set of tables for local call rates on different
+days of the week & times per day).
+.It Li recoverytime
+The time in seconds to wait between dial retries.
+(optional)
+.It Li remdial-handling
+is used to specify the dialout behavior in case more than one outgoing
+number is specified.
+The currently supported parameters are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar first
+For every new (non-retry) call setup, start with the first number.
+.It Ar last
+For every new (non-retry) call setup, start with the last number with
+which a successful connection was made.
+.It Ar next
+For every new (non-retry) call setup, start with the next number which
+follows the last one used.
+.El
+.It Li remote-phone-dialout
+The remote telephone number used when the local site dials out.
+When dialing
+out to a remote site, the number specified here is put into the
+.Em "Called Party Number Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+It may be specified more than once to try to dial to several
+numbers until one succeeds.
+.It Li remote-subaddr-dialout
+The remote subaddress used when the local site dials out.
+When dialing
+out to a remote site, the subaddress specified here is put into the
+.Em "Called Party Subaddress Information Element" .
+.Pp
+This keyword is mandatory for the
+.Em ipr
+interfaces.
+It may be specified more than once to linked it to the
+remote-phone-dialout numbers until one succeeds.
+.It Li remote-phone-incoming
+The remote telephone number used to verify an incoming call.
+When a remote site
+dials in, this number is used to verify that it is the correct remote site
+which is herewith authorized to connect into the local system.
+This parameter
+is compared against the
+.Em "Calling Party Number Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the ipr interfaces.
+.Pp
+This keyword may have a wildcard parameter '*' to permit anyone dialing in.
+.It Li remote-subaddr-incoming
+The remote subaddress used to verify an incoming call.
+When a remote site
+dials in, this subaddress is used to verify that it is the correct remote site
+which is herewith authorized to connect into the local system.
+This parameter
+is compared against the
+.Em "Calling Party Subaddress Information Element"
+got from the telephone exchange.
+.Pp
+This keyword is mandatory for the ipr interfaces.
+.Pp
+This keyword may have a wildcard parameter '*' to permit anyone dialing in.
+.It Li unitlength
+The length of a charging unit in seconds.
+This is used in conjunction with
+the idletime to decide when to hang up a connection.
+(optional)
+.It Li unitlengthsrc
+This keyword is used to specify from which source
+.Xr isdnd 8
+takes the unitlength for short-hold mode.
+The currently configurable values are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar none
+Then unitlength is not specified anywhere.
+.It Ar cmdl
+Use the unitlength specified on the command line.
+.It Ar conf
+Use the unitlength specified in the configuration file with the keyword
+.Em unitlength .
+.It Ar rate
+Use the unitlength from the ratesfile specified in the configuration
+file with the keyword
+.Em ratetype .
+.It Ar aocd
+Use a dynamically calculated unitlength in case AOCD is subscribed on
+the ISDN line.
+(AOCD is an acronym for ``Advice Of Charge During the call''
+which is a service provided by the telecommunications (ie phone) provider,
+to indicate billable units).
+.El
+.It Li usrdevicename
+Specifies the user-land interface which is used for interfacing ISDN B channel
+data to the user-land.
+The keyword is mandatory.
+This keyword accepts the following parameters:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar ipr
+This parameter configures a raw HDLC IP over ISDN interface.
+.It Ar isp
+This parameter configures a synchronous PPP over ISDN interface.
+.It Ar rbch
+This specifies a Raw B CHannel access interface.
+.It Ar tel
+ISDN telephony.
+.It Ar ing
+configures an ISDN B-channel to NetGraph interface.
+.El
+.It Li usrdeviceunit
+Specifies the unit number for the device which is specified with
+usrdevicename.
+.It Li usedown
+is used to enable the use of the keywords
+.Em downtries
+and
+.Em downtime
+in the entries section(s).
+It is used in the
+.Nm isdnd
+daemon to dynamically enable and disable the IP interfaces to avoid excessive
+dialing activities in case of transient failures (such as busy lines).
+This parameter is optional and is set to
+.Em off
+by default.
+.It Li usesubaddr
+is used to enable the use of subaddresses.
+This parameter is optional and is set to
+.Em off
+by default.
+.It Li valid
+.Em Note :
+this feature is considered experimental!
+The parameter to this keyword is a string specifying a time range within
+which this entry is valid.
+The time specification consists of a list of weekdays and/or a holiday
+indicator ( see also the
+.Em holidayfile
+keyword in the system section ) separated by commas followed by an optional
+daytime range specification in the form hh:mm-hh:mm.
+The weekdays are specified as numbers from 0 to 6 and the number 7 for
+holidays:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ar 0
+Sunday
+.It Ar 1
+Monday
+.It Ar 2
+Tuesday
+.It Ar 3
+Wednesday
+.It Ar 4
+Thursday
+.It Ar 5
+Friday
+.It Ar 6
+Saturday
+.It Ar 7
+a Holiday
+.El
+.Pp
+The following examples describe the "T-ISDN xxl" tariff of the german Telekom:
+.Bl -tag -width Ds -compact
+.It Ar 1,2,3,4,5,6,09:00-18:00
+Monday through Saturday, daytime 9:00 to 18:00
+.It Ar 1,2,3,4,5,6,18:00-9:00
+Monday through Saturday, nighttime 18:00 to 9:00
+.It Ar 0,7
+Sunday and on holidays, all 24 hours
+.El
+.Pp
+The use of this keyword is optional.
+.El
+.El
+.Sh IDLETIME CALCULATION AND SHORT-HOLD MODE
+.Bl -tag -width "incoming calls
+.It Li incoming calls
+It is assumed that the calling side knows most about charging structures and
+such and as a consequence only the keyword
+.Em idletime-incoming
+has a function for incoming calls.
+.Pp
+For incoming calls the line is constantly monitored, and in case there was
+not traffic taking place for the time in seconds specified by
+.Em idletime-incoming
+the call is closed.
+.Pp
+Typically,
+.Em idletime-incoming
+is used as a last resort and is therefore set much higher than a charging
+unit time: typical values are one to five minutes.
+.It Li outgoing calls
+Outgoing call disconnect time can be setup in one of three ways:
+.Bl -tag -width "shorthold mode
+.It Li simple mode
+For simple mode, the
+.Em idle-algorithm-outgoing
+must be
+.Em fix-unit-size
+and the selected
+.Em unitlength
+must be 0 (zero) and
+.Em idletime-outgoing
+greater zero.
+.Pp
+The outgoing traffic is constantly monitored, and in case there was
+not traffic taking place for the time in seconds specified by
+.Em idletime-outgoing
+the call is closed.
+.Pp
+Typical values in simple mode are 10 to 30 seconds.
+.It Li shorthold mode for fixed unit charging
+For shorthold mode, the
+.Em idle-algorithm-outgoing
+must be
+.Em fix-unit-size
+and the selected
+.Em unitlength
+and
+.Em idletime-outgoing
+must be greater than 0 (zero);
+.Em earlyhangup
+must be >= 0 (zero).
+.Bd -literal
+|<unchecked-window>|<checkwindow>|<safetywindow>|
+| | | |
++------------------+-------------+--------------+
+| | | |
+| |<-idle-time->|<earlyhangup->|
+|<--------------unitlength--------------------->|
+.Ed
+.Pp
+During the unchecked window which is (unitlength - (idle-time+earlyhangup))
+in length, no idle check is done.
+After the unchecked window has ended,
+the line is checked for idle-time length if no traffic takes place.
+In case
+there was traffic detected in the check-window, the same procedure is restarted
+at the beginning of the next unit.
+In case no traffic was detected during
+the check-window, the line is closed at the end of the check window.
+.Pp
+Notice:
+.Em unitlength
+must (!) be greater than the sum of
+.Em idletime-outgoing
+and
+.Em earlyhangup !
+.It Li shorthold mode for variable unit charging
+For shorthold mode, the
+.Em idle-algorithm-outgoing
+must be
+.Em var-unit-size
+and the selected
+.Em unitlength
+and
+.Em idletime-outgoing
+must be greater than 0 (zero);
+.Pp
+This shorthold mode is suitable when your calls are billed on
+the elapse time of the call plus a fixed connection charge.
+For example British Telecom bill this way.
+.Pp
+Each call is divided into two periods, the first is the
+.Em unchecked
+period and the second is the
+.Em checked .
+The
+.Em checked
+period starts 1 second before the first units time expires.
+.Pp
+During the
+.Em checked
+period if there is no traffic for
+.Em idle-time
+seconds the call is disconnected.
+.Pp
+.Bd -literal
+|<---unchecked------------------>|<------checked------>
++------------------+-------------+
+| |<-idle-time->|
+|<--------------unitlength------->|
+.Ed
+.Pp
+Experience shows that useful values for idle-time are from 15 to 30 seconds.
+.Pp
+If idle-time is too short an application that is not yet finished with the
+network will cause a new call to be placed.
+.Pp
+.El
+.El
+.Sh FILES
+.Bl -tag -width /etc/isdn/isdnd.rc -compact
+.It Pa /etc/isdn/isdnd.rc
+The default configuration file for the
+.Nm isdnd
+ISDN daemon.
+.El
+.Sh SEE ALSO
+.Xr regex 3 ,
+.Xr re_format 7 ,
+.Xr isdnd 8 ,
+.Xr isdnmonitor 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Xr isdnd 8
+daemon and this manual page were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
+.Pp
+Additions to this manual page by
+.An Barry Scott Aq barry@scottb.demon.co.uk .
diff --git a/usr.sbin/i4b/isdnd/log.c b/usr.sbin/i4b/isdnd/log.c
new file mode 100644
index 0000000..7876f13
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/log.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - logging routines
+ * -----------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Dec 26 12:49:45 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#define LOGBUFLEN 256
+
+extern int do_monitor;
+extern int accepted;
+extern FILE *logfp;
+
+static void check_reg(char *logstring);
+
+struct logtab {
+ char *text;
+ int pri;
+};
+
+/*---------------------------------------------------------------------------*
+ * table for converting internal log levels into syslog levels
+ *---------------------------------------------------------------------------*/
+static struct logtab logtab[] = {
+ {"ERR", LOG_ERR}, /* error conditions */
+ {"WRN", LOG_WARNING}, /* warning conditions, nonfatal */
+ {"DMN", LOG_NOTICE}, /* significant conditions of the daemon */
+ {"CHD", LOG_INFO}, /* informational, call handling */
+ {"DBG", LOG_DEBUG}, /* debug messages */
+ {"MER", LOG_ERR}, /* monitor error conditions */
+ {"PKT", LOG_INFO} /* packet logging */
+};
+
+/*---------------------------------------------------------------------------*
+ * initialize logging
+ *---------------------------------------------------------------------------*/
+void
+init_log(void)
+{
+ int i;
+
+ if(uselogfile)
+ {
+ if((logfp = fopen(logfile, "a")) == NULL)
+ {
+ fprintf(stderr, "ERROR, cannot open logfile %s: %s\n",
+ logfile, strerror(errno));
+ exit(1);
+ }
+
+ /* set unbuffered operation */
+
+ setvbuf(logfp, (char *)NULL, _IONBF, 0);
+ }
+ else
+ {
+#if DEBUG
+ if(do_debug && do_fork == 0 && do_fullscreen == 0)
+ (void)openlog("isdnd",
+ LOG_PID|LOG_CONS|LOG_NDELAY|LOG_PERROR,
+ logfacility);
+ else
+#endif
+ (void)openlog("isdnd", LOG_PID|LOG_CONS|LOG_NDELAY,
+ logfacility);
+ }
+
+ /* initialize the regexp array */
+
+ for(i = 0; i < MAX_RE; i++)
+ {
+ char *p;
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "%s%d", REGPROG_DEF, i);
+
+ rarr[i].re_flg = 0;
+
+ if((p = malloc(strlen(buf) + 1)) == NULL)
+ {
+ log(LL_DBG, "init_log: malloc failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ strcpy(p, buf);
+
+ rarr[i].re_prog = p;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * finish logging
+ *---------------------------------------------------------------------------*/
+void
+finish_log(void)
+{
+ if(uselogfile && logfp)
+ {
+ fflush(logfp);
+ fclose(logfp);
+ }
+ else
+ {
+ (void)closelog();
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * place entry into logfile
+ *---------------------------------------------------------------------------*/
+void
+log(int what, const char *fmt, ...)
+{
+ char buffer[LOGBUFLEN];
+ register char *dp;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, LOGBUFLEN-1, fmt, ap);
+ va_end(ap);
+
+ dp = getlogdatetime(); /* get time string ptr */
+
+#ifdef USE_CURSES
+
+ /* put log on screen ? */
+
+ if((do_fullscreen && curses_ready) &&
+ ((!debug_noscreen) || (debug_noscreen && (what != LL_DBG))))
+ {
+ wprintw(lower_w, "%s %s %-.*s\n", dp, logtab[what].text,
+
+/*
+ * FreeBSD-current integrated ncurses. Since then it is no longer possible
+ * to write to the last column in the logfilewindow without causing an
+ * automatic newline to occur resulting in a blank line in that window.
+ */
+#ifdef __FreeBSD__
+#include <osreldate.h>
+#endif
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 400009
+#warning "FreeBSD ncurses is buggy: write to last column = auto newline!"
+ COLS-((strlen(dp))+(strlen(logtab[what].text))+3), buffer);
+#else
+ (int)(COLS-((strlen(dp))+(strlen(logtab[what].text))+2)), buffer);
+#endif
+ wrefresh(lower_w);
+ }
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(what != LL_MER) /* don't send monitor errs, endless loop !!! */
+ monitor_evnt_log(logtab[what].pri, logtab[what].text, buffer);
+#endif
+
+ if(uselogfile)
+ {
+ fprintf(logfp, "%s %s %s\n", dp, logtab[what].text, buffer);
+ }
+ else
+ {
+ register char *s = buffer;
+
+ /* strip leading spaces from syslog output */
+
+ while(*s && (*s == ' '))
+ s++;
+
+ syslog(logtab[what].pri, "%s %s", logtab[what].text, s);
+ }
+
+
+#if DEBUG
+ if(what != LL_DBG) /* don't check debug logs, endless loop !!! */
+#endif
+ check_reg(buffer);
+}
+
+/*---------------------------------------------------------------------------*
+ * return ptr to static area containing date/time
+ *---------------------------------------------------------------------------*/
+char *
+getlogdatetime()
+{
+ static char logdatetime[41];
+ time_t tim;
+ register struct tm *tp;
+
+ tim = time(NULL);
+ tp = localtime(&tim);
+ strftime(logdatetime,40,I4B_TIME_FORMAT,tp);
+ return(logdatetime);
+}
+
+/*---------------------------------------------------------------------------*
+ * check for a match in the regexp array
+ *---------------------------------------------------------------------------*/
+static void
+check_reg(char *logstring)
+{
+ register int i;
+
+ for(i = 0; i < MAX_RE; i++)
+ {
+ if(rarr[i].re_flg && (!regexec(&(rarr[i].re), logstring, (size_t) 0, NULL, 0)))
+ {
+ char* argv[3];
+ argv[0] = rarr[i].re_prog;
+ argv[1] = logstring;
+ argv[2] = NULL;
+
+ exec_prog(rarr[i].re_prog, argv);
+ break;
+ }
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/main.c b/usr.sbin/i4b/isdnd/main.c
new file mode 100644
index 0000000..74e1e63
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/main.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - main program entry
+ * -------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Dec 26 12:51:00 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <locale.h>
+#include <paths.h>
+
+#ifdef I4B_EXTERNAL_MONITOR
+#include "monitor.h"
+#endif
+
+#define MAIN
+#include "isdnd.h"
+#undef MAIN
+
+#ifdef I4B_EXTERNAL_MONITOR
+
+#ifdef I4B_NOTCPIP_MONITOR
+/* monitor via local socket */
+static void mloop(int sockfd);
+#else /* I4B_NOTCPIP_MONITOR */
+/* monitor via local and tcp/ip socket */
+static void mloop(int localsock, int remotesock);
+#endif /* I4B_NOTCPIP_MONITOR */
+
+#else /* I4B_EXTERNAL_MONITOR */
+/* no monitoring at all */
+static void mloop();
+#endif /* I4B_EXTERNAL_MONITOR */
+
+#ifdef USE_CURSES
+static void kbdrdhdl(void);
+#endif
+
+static void isdnrdhdl(void);
+static void usage(void);
+
+#define MSG_BUF_SIZ 1024 /* message buffer size */
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdnd - i4b ISDN manager daemon, version %02d.%02d.%d, %s %s\n", VERSION, REL, STEP, __DATE__, __TIME__);
+#ifdef DEBUG
+ fprintf(stderr, " usage: isdnd [-c file] [-d level] [-F] [-f [-r dev] [-t termtype]]\n");
+#else
+ fprintf(stderr, " usage: isdnd [-c file] [-F] [-f [-r dev] [-t termtype]]\n");
+#endif
+ fprintf(stderr, " [-l] [-L file] [-m] [-s facility] [-u time]\n");
+ fprintf(stderr, " -c <filename> configuration file name (def: %s)\n", CONFIG_FILE_DEF);
+#ifdef DEBUG
+ fprintf(stderr, " -d <level> set debug flag bits:\n");
+ fprintf(stderr, " general = 0x%04x, rates = 0x%04x, timing = 0x%04x\n", DL_MSG, DL_RATES, DL_TIME);
+ fprintf(stderr, " state = 0x%04x, retry = 0x%04x, dial = 0x%04x\n", DL_STATE, DL_RCVRY, DL_DIAL);
+ fprintf(stderr, " process = 0x%04x, kernio = 0x%04x, ctrlstat = 0x%04x\n", DL_PROC, DL_DRVR, DL_CNST);
+ fprintf(stderr, " rc-file = 0x%04x, budget = 0x%04x, valid = 0x%04x\n", DL_RCCF, DL_BDGT, DL_VALID);
+ fprintf(stderr, " -dn no debug output on fullscreen display\n");
+#endif
+ fprintf(stderr, " -f fullscreen status display\n");
+ fprintf(stderr, " -F do not become a daemon process\n");
+ fprintf(stderr, " -l use a logfile instead of syslog\n");
+ fprintf(stderr, " -L <file> use file instead of %s for logging\n", LOG_FILE_DEF);
+ fprintf(stderr, " -P pretty print real config to stdout and exit\n");
+ fprintf(stderr, " -r <device> redirect output to other device (for -f)\n");
+ fprintf(stderr, " -s <facility> use facility instead of %d for syslog logging\n", LOG_LOCAL0 >> 3);
+ fprintf(stderr, " -t <termtype> terminal type of redirected screen (for -f)\n");
+ fprintf(stderr, " -u <time> length of a charging unit in seconds\n");
+#ifdef I4B_EXTERNAL_MONITOR
+ fprintf(stderr, " -m inhibit network/local monitoring (protocol %02d.%02d)\n", MPROT_VERSION, MPROT_REL);
+#endif
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int i;
+ msg_vr_req_t mvr;
+
+#ifdef I4B_EXTERNAL_MONITOR
+ int sockfd = -1; /* local monitor socket */
+#ifndef I4B_NOTCPIP_MONITOR
+ int remotesockfd = -1; /* tcp/ip monitor socket */
+#endif
+#endif
+
+ setlocale (LC_ALL, "");
+
+ while ((i = getopt(argc, argv, "mc:d:fFlL:Pr:s:t:u:")) != -1)
+ {
+ switch (i)
+ {
+#ifdef I4B_EXTERNAL_MONITOR
+ case 'm':
+ inhibit_monitor = 1;
+ break;
+#endif
+
+ case 'c':
+ configfile = optarg;
+ break;
+
+#ifdef DEBUG
+ case 'd':
+ if(*optarg == 'n')
+ debug_noscreen = 1;
+ else if((sscanf(optarg, "%i", &debug_flags)) == 1)
+ do_debug = 1;
+ else
+ usage();
+ break;
+#endif
+
+ case 'f':
+ do_fullscreen = 1;
+ do_fork = 0;
+#ifndef USE_CURSES
+ fprintf(stderr, "Sorry, no fullscreen mode available - daemon compiled without USE_CURSES\n");
+ exit(1);
+#endif
+ break;
+
+ case 'F':
+ do_fork = 0;
+ break;
+
+ case 'l':
+ uselogfile = 1;
+ break;
+
+ case 'L':
+ strlcpy(logfile, optarg, sizeof(logfile));
+ break;
+
+ case 'P':
+ do_print = 1;
+ break;
+
+ case 'r':
+ rdev = optarg;
+ do_rdev = 1;
+ break;
+
+ case 's':
+ if(isdigit(*optarg))
+ {
+ int facility;
+ logfacility = strtoul(optarg, NULL, 10);
+ facility = logfacility << 3;
+
+ if((facility < LOG_KERN) ||
+ (facility > LOG_FTP && facility < LOG_LOCAL0) ||
+ (facility > LOG_LOCAL7))
+ {
+ fprintf(stderr, "Error, option -s has invalid logging facility %d", logfacility);
+ usage();
+ }
+ logfacility = facility;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -s requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case 't':
+ ttype = optarg;
+ do_ttytype = 1;
+ break;
+
+ case 'u':
+ if(isdigit(*optarg))
+ {
+ unit_length = strtoul(optarg, NULL, 10);
+ if(unit_length < ULSRC_CMDLMIN)
+ unit_length = ULSRC_CMDLMIN;
+ else if(unit_length > ULSRC_CMDLMAX)
+ unit_length = ULSRC_CMDLMAX;
+ got_unitlen = 1;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -T requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+#ifdef DEBUG
+ if(!do_debug)
+ debug_noscreen = 0;
+#endif
+
+ if(!do_print)
+ {
+ umask(UMASK); /* set our umask ... */
+
+ init_log(); /* initialize the logging subsystem */
+ }
+
+ check_pid(); /* check if we are already running */
+
+ if(!do_print)
+ {
+ if(do_fork || (do_fullscreen && do_rdev)) /* daemon mode ? */
+ daemonize();
+
+ write_pid(); /* write our pid to file */
+
+ /* set signal handler(s) */
+
+ signal(SIGCHLD, sigchild_handler); /* process handling */
+ signal(SIGHUP, rereadconfig); /* reread configuration */
+ signal(SIGUSR1, reopenfiles); /* reopen acct/log files*/
+ signal(SIGPIPE, SIG_IGN); /* handled manually */
+ signal(SIGINT, do_exit); /* clean up on SIGINT */
+ signal(SIGTERM, do_exit); /* clean up on SIGTERM */
+ signal(SIGQUIT, do_exit); /* clean up on SIGQUIT */
+ }
+
+ /* open isdn device */
+
+ if((isdnfd = open(I4BDEVICE, O_RDWR)) < 0)
+ {
+ log(LL_ERR, "main: cannot open %s: %s", I4BDEVICE, strerror(errno));
+ exit(1);
+ }
+
+ /* check kernel and userland have same version/release numbers */
+
+ if((ioctl(isdnfd, I4B_VR_REQ, &mvr)) < 0)
+ {
+ log(LL_ERR, "main: ioctl I4B_VR_REQ failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ if(mvr.version != VERSION)
+ {
+ log(LL_ERR, "main: version mismatch, kernel %d, daemon %d", mvr.version, VERSION);
+ do_exit(1);
+ }
+
+ if(mvr.release != REL)
+ {
+ log(LL_ERR, "main: release mismatch, kernel %d, daemon %d", mvr.release, REL);
+ do_exit(1);
+ }
+
+ if(mvr.step != STEP)
+ {
+ log(LL_ERR, "main: step mismatch, kernel %d, daemon %d", mvr.step, STEP);
+ do_exit(1);
+ }
+
+ /* init controller state array */
+
+ init_controller();
+
+ /* read runtime configuration file and configure ourselves */
+
+ configure(configfile, 0);
+
+ if(config_error_flag)
+ {
+ log(LL_ERR, "there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ exit(1);
+ }
+
+ /* set controller ISDN protocol */
+
+ init_controller_protocol();
+
+ /* init active controllers, if any */
+
+ signal(SIGCHLD, SIG_IGN); /*XXX*/
+
+ init_active_controller();
+
+ signal(SIGCHLD, sigchild_handler); /*XXX*/
+
+ /* handle the rates stuff */
+
+ if((i = readrates(ratesfile)) == ERROR)
+ {
+ if(rate_error != NULL)
+ log(LL_ERR, "%s", rate_error);
+ exit(1);
+ }
+
+ if(i == GOOD)
+ {
+ got_rate = 1; /* flag, ratesfile read and ok */
+ DBGL(DL_RCCF, (log(LL_DBG, "ratesfile %s read successfully", ratesfile)));
+ }
+ else
+ {
+ if(rate_error != NULL)
+ log(LL_WRN, "%s", rate_error);
+ }
+
+ /* if writing accounting info, open file, set unbuffered */
+
+ if(useacctfile)
+ {
+ if((acctfp = fopen(acctfile, "a")) == NULL)
+ {
+ log(LL_ERR, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
+ exit(1);
+ }
+ setvbuf(acctfp, (char *)NULL, _IONBF, 0);
+ }
+
+ /* initialize alias processing */
+
+ if(aliasing)
+ init_alias(aliasfile);
+
+ /* init holidays */
+
+ init_holidays(holidayfile);
+
+ /* init remote monitoring */
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor)
+ {
+ monitor_init();
+ sockfd = monitor_create_local_socket();
+#ifndef I4B_NOTCPIP_MONITOR
+ remotesockfd = monitor_create_remote_socket(monitorport);
+#endif
+ }
+#endif
+
+ /* in case fullscreendisplay, initialize */
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ {
+ init_screen();
+ }
+#endif
+
+ /* init realtime priority */
+
+#ifdef USE_RTPRIO
+ if(rt_prio != RTPRIO_NOTUSED)
+ {
+ struct rtprio rtp;
+
+ rtp.type = RTP_PRIO_REALTIME;
+ rtp.prio = rt_prio;
+
+ if((rtprio(RTP_SET, getpid(), &rtp)) == -1)
+ {
+ log(LL_ERR, "rtprio failed: %s", strerror(errno));
+ do_exit(1);
+ }
+ }
+#endif
+
+ starttime = time(NULL); /* get starttime */
+
+ srandom(580403); /* init random number gen */
+
+ mloop( /* enter loop of no return .. */
+#ifdef I4B_EXTERNAL_MONITOR
+ sockfd
+#ifndef I4B_NOTCPIP_MONITOR
+ , remotesockfd
+#endif
+#endif
+ );
+ do_exit(0);
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * program exit
+ *---------------------------------------------------------------------------*/
+void
+do_exit(int exitval)
+{
+ close_allactive();
+
+ unlink(PIDFILE);
+
+ log(LL_DMN, "daemon terminating, exitval = %d", exitval);
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ endwin();
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_exit();
+#endif
+
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * program exit
+ *---------------------------------------------------------------------------*/
+void
+error_exit(int exitval, const char *fmt, ...)
+{
+ close_allactive();
+
+ unlink(PIDFILE);
+
+ log(LL_DMN, "fatal error, daemon terminating, exitval = %d", exitval);
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ endwin();
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_exit();
+#endif
+
+ if(mailto[0] && mailer[0])
+ {
+
+#define EXITBL 2048
+
+ char ebuffer[EXITBL];
+ char sbuffer[EXITBL];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(ebuffer, EXITBL-1, fmt, ap);
+ va_end(ap);
+
+ signal(SIGCHLD, SIG_IGN); /* remove handler */
+
+ snprintf(sbuffer, sizeof(sbuffer), "%s%s%s%s%s%s%s%s",
+ "cat << ENDOFDATA | ",
+ mailer,
+ " -s \"i4b isdnd: fatal error, terminating\" ",
+ mailto,
+ "\nThe isdnd terminated because of a fatal error:\n\n",
+ ebuffer,
+ "\n\nYours sincerely,\n the isdnd\n",
+ "\nENDOFDATA\n");
+ system(sbuffer);
+ }
+
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * main loop
+ *---------------------------------------------------------------------------*/
+static void
+mloop(
+#ifdef I4B_EXTERNAL_MONITOR
+ int localmonitor
+#ifndef I4B_NOTCPIP_MONITOR
+ , int remotemonitor
+#endif
+#endif
+)
+{
+ fd_set set;
+ struct timeval timeout;
+ int ret;
+ int high_selfd;
+
+ /* go into loop */
+
+ log(LL_DMN, "i4b isdn daemon started (pid = %d)", getpid());
+
+ for(;;)
+ {
+ FD_ZERO(&set);
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ FD_SET(fileno(stdin), &set);
+#endif
+
+ FD_SET(isdnfd, &set);
+
+ high_selfd = isdnfd;
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor)
+ {
+ if (localmonitor != -1) {
+ /* always watch for new connections */
+ FD_SET(localmonitor, &set);
+ if(localmonitor > high_selfd)
+ high_selfd = localmonitor;
+ }
+#ifndef I4B_NOTCPIP_MONITOR
+ if (remotemonitor != -1) {
+ FD_SET(remotemonitor, &set);
+ if(remotemonitor > high_selfd)
+ high_selfd = remotemonitor;
+ }
+#endif
+
+ /* if there are client connections, let monitor module
+ * enter them into the fdset */
+ if(accepted)
+ {
+ monitor_prepselect(&set, &high_selfd);
+ }
+ }
+#endif
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ret = select(high_selfd + 1, &set, NULL, NULL, &timeout);
+
+ if(ret > 0)
+ {
+ if(FD_ISSET(isdnfd, &set))
+ isdnrdhdl();
+
+#ifdef USE_CURSES
+ if(FD_ISSET(fileno(stdin), &set))
+ kbdrdhdl();
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor)
+ {
+ if(localmonitor != -1 && FD_ISSET(localmonitor, &set))
+ monitor_handle_connect(localmonitor, 1);
+
+#ifndef I4B_NOTCPIP_MONITOR
+ if(remotemonitor != -1 && FD_ISSET(remotemonitor, &set))
+ monitor_handle_connect(remotemonitor, 0);
+#endif
+ if(accepted)
+ monitor_handle_input(&set);
+ }
+#endif
+ }
+ else if(ret == -1)
+ {
+ if(errno != EINTR)
+ {
+ log(LL_ERR, "mloop: ERROR, select error on isdn device, errno = %d!", errno);
+ error_exit(1, "mloop: ERROR, select error on isdn device, errno = %d!", errno);
+ }
+ }
+
+ /* handle timeout and recovery */
+
+ handle_recovery();
+ }
+}
+
+#ifdef USE_CURSES
+/*---------------------------------------------------------------------------*
+ * data from keyboard available, read and process it
+ *---------------------------------------------------------------------------*/
+static void
+kbdrdhdl(void)
+{
+ int ch = getch();
+
+ if(ch == ERR)
+ {
+ log(LL_ERR, "kbdrdhdl: ERROR, read error on controlling tty, errno = %d!", errno);
+ error_exit(1, "kbdrdhdl: ERROR, read error on controlling tty, errno = %d!", errno);
+ }
+
+ switch(ch)
+ {
+ case 0x0c: /* control L */
+ wrefresh(curscr);
+ break;
+
+ case '\n':
+ case '\r':
+ do_menu();
+ break;
+ }
+}
+#endif
+
+/*---------------------------------------------------------------------------*
+ * data from /dev/isdn available, read and process them
+ *---------------------------------------------------------------------------*/
+static void
+isdnrdhdl(void)
+{
+ static unsigned char msg_rd_buf[MSG_BUF_SIZ];
+ msg_hdr_t *hp = (msg_hdr_t *)&msg_rd_buf[0];
+
+ register int len;
+
+ if((len = read(isdnfd, msg_rd_buf, MSG_BUF_SIZ)) > 0)
+ {
+ switch(hp->type)
+ {
+ case MSG_CONNECT_IND:
+ msg_connect_ind((msg_connect_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_CONNECT_ACTIVE_IND:
+ msg_connect_active_ind((msg_connect_active_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DISCONNECT_IND:
+ msg_disconnect_ind((msg_disconnect_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DIALOUT_IND:
+ msg_dialout((msg_dialout_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_ACCT_IND:
+ msg_accounting((msg_accounting_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_IDLE_TIMEOUT_IND:
+ msg_idle_timeout_ind((msg_idle_timeout_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_CHARGING_IND:
+ msg_charging_ind((msg_charging_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_PROCEEDING_IND:
+ msg_proceeding_ind((msg_proceeding_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_ALERT_IND:
+ msg_alert_ind((msg_alert_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DRVRDISC_REQ:
+ msg_drvrdisc_req((msg_drvrdisc_req_t *)msg_rd_buf);
+ break;
+
+ case MSG_L12STAT_IND:
+ msg_l12stat_ind((msg_l12stat_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_TEIASG_IND:
+ msg_teiasg_ind((msg_teiasg_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_PDEACT_IND:
+ msg_pdeact_ind((msg_pdeact_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_NEGCOMP_IND:
+ msg_negcomplete_ind((msg_negcomplete_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_IFSTATE_CHANGED_IND:
+ msg_ifstatechg_ind((msg_ifstatechg_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_DIALOUTNUMBER_IND:
+ msg_dialoutnumber((msg_dialoutnumber_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_PACKET_IND:
+ msg_packet_ind((msg_packet_ind_t *)msg_rd_buf);
+ break;
+
+ case MSG_KEYPAD_IND:
+ msg_keypad((msg_keypad_ind_t *)msg_rd_buf);
+ break;
+
+ default:
+ log(LL_WRN, "ERROR, unknown message received from %sisdn (0x%x)", _PATH_DEV, msg_rd_buf[0]);
+ break;
+ }
+ }
+ else
+ {
+ log(LL_WRN, "ERROR, read error on isdn device, errno = %d, length = %d", errno, len);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * re-read the config file on SIGHUP or menu command
+ *---------------------------------------------------------------------------*/
+void
+rereadconfig(int dummy)
+{
+ extern int entrycount;
+
+ log(LL_DMN, "re-reading configuration file");
+
+ close_allactive();
+
+#if I4B_EXTERNAL_MONITOR
+ monitor_clear_rights();
+#endif
+
+ entrycount = -1;
+ nentries = 0;
+
+ /* read runtime configuration file and configure ourselves */
+
+ configure(configfile, 1);
+
+ if(config_error_flag)
+ {
+ log(LL_ERR, "rereadconfig: there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ error_exit(1, "rereadconfig: there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ }
+
+ if(aliasing)
+ {
+ /* reread alias database */
+ free_aliases();
+ init_alias(aliasfile);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * re-open the log/acct files on SIGUSR1
+ *---------------------------------------------------------------------------*/
+void
+reopenfiles(int dummy)
+{
+ if(useacctfile)
+ {
+ /* close file */
+
+ if(acctfp)
+ {
+ fflush(acctfp);
+ fclose(acctfp);
+ }
+
+ /* if user specified a suffix, rename the old file */
+
+ if(rotatesuffix[0] != '\0')
+ {
+ char filename[MAXPATHLEN];
+
+ snprintf(filename, sizeof(filename), "%s%s", acctfile, rotatesuffix);
+
+ if((rename(acctfile, filename)) != 0)
+ {
+ log(LL_ERR, "reopenfiles: acct rename failed, cause = %s", strerror(errno));
+ error_exit(1, "reopenfiles: acct rename failed, cause = %s", strerror(errno));
+ }
+ }
+
+ if((acctfp = fopen(acctfile, "a")) == NULL)
+ {
+ log(LL_ERR, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
+ error_exit(1, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
+ }
+ setvbuf(acctfp, (char *)NULL, _IONBF, 0);
+ }
+
+ if(uselogfile)
+ {
+ finish_log();
+
+ /* if user specified a suffix, rename the old file */
+
+ if(rotatesuffix[0] != '\0')
+ {
+ char filename[MAXPATHLEN];
+
+ snprintf(filename, sizeof(filename), "%s%s", logfile, rotatesuffix);
+
+ if((rename(logfile, filename)) != 0)
+ {
+ log(LL_ERR, "reopenfiles: log rename failed, cause = %s", strerror(errno));
+ error_exit(1, "reopenfiles: log rename failed, cause = %s", strerror(errno));
+ }
+ }
+
+ if((logfp = fopen(logfile, "a")) == NULL)
+ {
+ fprintf(stderr, "ERROR, cannot open logfile %s: %s\n",
+ logfile, strerror(errno));
+ error_exit(1, "reopenfiles: ERROR, cannot open logfile %s: %s\n",
+ logfile, strerror(errno));
+ }
+
+ /* set unbuffered operation */
+
+ setvbuf(logfp, (char *)NULL, _IONBF, 0);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/monitor.c b/usr.sbin/i4b/isdnd/monitor.c
new file mode 100644
index 0000000..45952eb
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/monitor.c
@@ -0,0 +1,1284 @@
+/*
+ * Copyright (c) 1998, 1999 Martin Husemann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - network monitor server module
+ * ------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:37:03 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#ifndef I4B_EXTERNAL_MONITOR
+
+/*
+ * dummy version of routines needed by config file parser
+ * (config files should be valid with and without external montioring
+ * support compiled into the daemon)
+ */
+
+void monitor_clear_rights()
+{ }
+
+int monitor_start_rights(const char *clientspec)
+{ return I4BMAR_OK; }
+
+void monitor_add_rights(int rights_mask)
+{ }
+
+void monitor_fixup_rights()
+{ }
+
+#else
+
+#include "monitor.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#ifndef I4B_NOTCPIP_MONITOR
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+
+static TAILQ_HEAD(rights_q, monitor_rights) rights = TAILQ_HEAD_INITIALIZER(rights);
+
+static struct monitor_rights * local_rights = NULL; /* entry for local socket */
+
+/* for each active monitor connection we have one of this: */
+
+struct monitor_connection {
+ TAILQ_ENTRY(monitor_connection) connections;
+ int sock; /* socket for this connection */
+ int rights; /* active rights for this connection */
+ int events; /* bitmask of events client is interested in */
+ char source[FILENAME_MAX];
+};
+
+static TAILQ_HEAD(connections_tq, monitor_connection) connections = TAILQ_HEAD_INITIALIZER(connections);
+
+/* local prototypes */
+static int cmp_rights(const struct monitor_rights *pa, const struct monitor_rights *pb);
+static int monitor_command(struct monitor_connection *con, int fd, int rights);
+static void cmd_dump_rights(int fd, int rights, u_int8_t *cmd, const char * source);
+static void cmd_dump_mcons(int fd, int rights, u_int8_t *cmd, const char * source);
+static void cmd_reread_cfg(int fd, int rights, u_int8_t *cmd, const char * source);
+static void cmd_hangup(int fd, int rights, u_int8_t *cmd, const char * source);
+static void monitor_broadcast(int mask, u_int8_t *pkt, size_t bytes);
+static int anybody(int mask);
+static void hangup_channel(int controller, int channel, const char *source);
+static ssize_t sock_read(int fd, void *buf, size_t nbytes);
+static ssize_t sock_write(int fd, void *buf, size_t nbytes);
+
+/*
+ * Due to the way we structure config files, the rights for an external
+ * monitor might be stated in multiple steps. First a call to
+ * monitor_start_rights opens an entry. Further (optional) calls to
+ * montior_add_rights assemble additional rights for this "current"
+ * entry. When closing the sys-file section of the config file, the
+ * "current" entry becomes invalid.
+ */
+static struct monitor_rights * cur_add_entry = NULL;
+
+/*---------------------------------------------------------------------------
+ * Initialize the monitor server module. This affects only active
+ * connections, the access rights are not modified here!
+ *---------------------------------------------------------------------------*/
+void
+monitor_init(void)
+{
+ struct monitor_connection * con;
+ accepted = 0;
+ while ((con = TAILQ_FIRST(&connections)) != NULL)
+ {
+ TAILQ_REMOVE(&connections, con, connections);
+ free(con);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Prepare for exit
+ *---------------------------------------------------------------------------*/
+void
+monitor_exit(void)
+{
+ struct monitor_connection *c;
+
+ /* Close all open connections. */
+ while((c = TAILQ_FIRST(&connections)) != NULL) {
+ close(c->sock);
+ TAILQ_REMOVE(&connections, c, connections);
+ free(c);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Initialize access rights. No active connections are affected!
+ *---------------------------------------------------------------------------*/
+void
+monitor_clear_rights(void)
+{
+ struct monitor_rights *r;
+ while ((r = TAILQ_FIRST(&rights)) != NULL) {
+ TAILQ_REMOVE(&rights, r, list);
+ free(r);
+ }
+ cur_add_entry = NULL;
+ local_rights = NULL;
+}
+
+/*---------------------------------------------------------------------------
+ * Add an entry to the access lists. The clientspec either is
+ * the name of the local socket or a host- or networkname or
+ * numeric ip/host-bit-len spec.
+ *---------------------------------------------------------------------------*/
+int
+monitor_start_rights(const char *clientspec)
+{
+ struct monitor_rights r;
+
+ /* initialize the new rights entry */
+
+ memset(&r, 0, sizeof r);
+
+ /* check clientspec */
+
+ if (*clientspec == '/')
+ {
+ struct sockaddr_un sa;
+
+ /* this is a local socket spec, check if we already have one */
+
+ if (local_rights != NULL)
+ return I4BMAR_DUP;
+
+ /* does it fit in a local socket address? */
+
+ if (strlen(clientspec) > sizeof sa.sun_path)
+ return I4BMAR_LENGTH;
+
+ r.local = 1;
+ strcpy(r.name, clientspec);
+
+#ifndef I4B_NOTCPIP_MONITOR
+
+ }
+ else
+ {
+ /* remote entry, parse host/net and cidr */
+
+ struct monitor_rights * rp;
+ char hostname[FILENAME_MAX];
+ char *p;
+
+ p = strchr(clientspec, '/');
+
+ if (!p)
+ {
+ struct hostent *host;
+ u_int32_t hn;
+
+ /* must be a host spec */
+
+ r.mask = ~0;
+ host = gethostbyname(clientspec);
+
+ if (!host)
+ return I4BMAR_NOIP;
+
+ memcpy(&hn, host->h_addr_list[0], sizeof hn);
+ r.net = (u_int32_t)ntohl(hn);
+ }
+ else if(p[1])
+ {
+ /* must be net/cidr spec */
+
+ int l;
+ struct netent *net;
+ u_int32_t s = ~0U;
+ int num = strtol(p+1, NULL, 10);
+
+ if (num < 0 || num > 32)
+ return I4BMAR_CIDR;
+
+ s >>= num;
+ s ^= ~0U;
+ l = p - clientspec;
+
+ if (l >= sizeof hostname)
+ return I4BMAR_LENGTH;
+
+ strncpy(hostname, clientspec, l);
+
+ hostname[l] = '\0';
+
+ net = getnetbyname(hostname);
+
+ if (net == NULL)
+ r.net = (u_int32_t)inet_network(hostname);
+ else
+ r.net = (u_int32_t)net->n_net;
+
+ r.mask = s;
+ r.net &= s;
+ }
+ else
+ {
+ return I4BMAR_CIDR;
+ }
+
+ /* check for duplicate entry */
+
+ for (rp = TAILQ_FIRST(&rights); rp != NULL; rp = TAILQ_NEXT(rp, list))
+ {
+ if (rp->mask == r.mask &&
+ rp->net == r.net &&
+ rp->local == r.local)
+ {
+ return I4BMAR_DUP;
+ }
+ }
+#endif
+ }
+
+ r.rights = 0;
+
+ /* entry ok, add it to the collection */
+
+ cur_add_entry = malloc(sizeof(r));
+ memcpy(cur_add_entry, &r, sizeof(r));
+ TAILQ_INSERT_TAIL(&rights, cur_add_entry, list);
+
+ if(r.local)
+ local_rights = cur_add_entry;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor = %s", clientspec)));
+
+ return I4BMAR_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * Add rights to the currently constructed entry - if any.
+ *---------------------------------------------------------------------------*/
+void
+monitor_add_rights(int rights_mask)
+{
+ if(cur_add_entry == NULL)
+ return; /* noone under construction */
+
+ cur_add_entry->rights |= rights_mask;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-access = 0x%x", rights_mask)));
+}
+
+/*---------------------------------------------------------------------------
+ * All rights have been added now. Sort the to get most specific
+ * host/net masks first, so we can travel the list and use the first
+ * match for actual rights.
+ *---------------------------------------------------------------------------*/
+void
+monitor_fixup_rights(void)
+{
+ struct monitor_rights * cur, * test, * next;
+
+ /* no more rights may be added to the current entry */
+
+ cur_add_entry = NULL;
+
+ /* sort the rights */
+ for (next = NULL, cur = TAILQ_FIRST(&rights); cur != NULL; cur = next)
+ {
+ next = TAILQ_NEXT(cur, list);
+ for (test = TAILQ_FIRST(&rights); test != NULL && test != cur; test = TAILQ_NEXT(test, list))
+ {
+ if (cmp_rights(cur, test) > 0) {
+ /* move cur up the list and insert before test */
+ TAILQ_REMOVE(&rights, cur, list);
+ if (test == TAILQ_FIRST(&rights))
+ TAILQ_INSERT_HEAD(&rights, cur, list);
+ else
+ TAILQ_INSERT_BEFORE(test, cur, list);
+ break;
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * comparator for rights
+ *---------------------------------------------------------------------------*/
+static int
+cmp_rights(const struct monitor_rights *pa, const struct monitor_rights *pb)
+{
+ u_int32_t mask;
+
+ /* local sorts first */
+
+ if (pa->local)
+ return -1;
+
+ /* which is the less specific netmask? */
+
+ mask = pa->mask;
+
+ if ((pb->mask & mask) == 0)
+ mask = pb->mask;
+
+ /* are the entries disjunct? */
+
+ if ((pa->net & mask) != (pb->net & mask))
+ {
+ /* simply compare net part of address */
+ return ((pa->net & mask) < (pb->net & mask)) ? -1 : 1;
+ }
+
+ /* One entry is part of the others net. We already now "mask" is
+ * the netmask of the less specific (i.e. greater) one */
+
+ return (pa->mask == mask) ? 1 : -1;
+}
+
+#ifndef I4B_NOTCPIP_MONITOR
+/*---------------------------------------------------------------------------
+ * Check if access rights for a remote socket are specified and
+ * create this socket. Return -1 otherwise.
+ *---------------------------------------------------------------------------*/
+int
+monitor_create_remote_socket(int portno)
+{
+ struct sockaddr_in sa;
+ int val;
+ int remotesockfd;
+
+ remotesockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ if(remotesockfd == -1)
+ {
+ log(LL_MER, "could not create remote monitor socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ val = 1;
+
+ if(setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val))
+ {
+ log(LL_MER, "could not setsockopt: %s", strerror(errno));
+ return(-1);
+ }
+
+ memset(&sa, 0, sizeof sa);
+ sa.sin_len = sizeof sa;
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portno);
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if(bind(remotesockfd, (struct sockaddr *)&sa, sizeof sa) == -1)
+ {
+ log(LL_MER, "could not bind remote monitor socket to port %d: %s", portno, strerror(errno));
+ return(-1);
+ }
+
+ if(listen(remotesockfd, 0))
+ {
+ log(LL_MER, "could not listen on monitor socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ return(remotesockfd);
+}
+#endif
+
+/*---------------------------------------------------------------------------
+ * Check if access rights for a local socket are specified and
+ * create this socket. Return -1 otherwise.
+ *---------------------------------------------------------------------------*/
+int
+monitor_create_local_socket(void)
+{
+ int s;
+ struct sockaddr_un sa;
+
+ /* check for a local entry */
+
+ if (local_rights == NULL)
+ return(-1);
+
+ /* create and setup socket */
+
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+ if (s == -1)
+ {
+ log(LL_MER, "could not create local monitor socket, errno = %d", errno);
+ return(-1);
+ }
+
+ unlink(local_rights->name);
+
+ memset(&sa, 0, sizeof sa);
+ sa.sun_len = sizeof sa;
+ sa.sun_family = AF_LOCAL;
+ strcpy(sa.sun_path, local_rights->name);
+
+ if (bind(s, (struct sockaddr *)&sa, SUN_LEN(&sa)))
+ {
+ log(LL_MER, "could not bind local monitor socket [%s], errno = %d", local_rights->name, errno);
+ return(-1);
+ }
+
+ chmod(local_rights->name, 0500);
+
+ if (listen(s, 0))
+ {
+ log(LL_MER, "could not listen on local monitor socket, errno = %d", errno);
+ return(-1);
+ }
+
+ return(s);
+}
+
+/*---------------------------------------------------------------------------
+ * Prepare a fd_set for a select call. Add all our local
+ * filedescriptors to the set, increment max_fd if appropriate.
+ *---------------------------------------------------------------------------*/
+void
+monitor_prepselect(fd_set *selset, int *max_fd)
+{
+ struct monitor_connection * con;
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+ int fd = con->sock;
+
+ if (fd > *max_fd)
+ *max_fd = fd;
+
+ FD_SET(fd, selset);
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Check if the result from a select call indicates something
+ * to do for us.
+ *---------------------------------------------------------------------------*/
+void
+monitor_handle_input(fd_set *selset)
+{
+ struct monitor_connection * con, * next;
+
+ for (next = NULL, con = TAILQ_FIRST(&connections); con != NULL; con = next)
+ {
+ int fd = con->sock;
+ next = TAILQ_NEXT(con, connections);
+
+ if (FD_ISSET(fd, selset))
+ {
+ /* handle command from this client */
+
+ if (monitor_command(con, fd, con->rights) != 0)
+ {
+ /* broken or closed connection */
+
+ char source[FILENAME_MAX];
+
+ strcpy(source, con->source);
+ TAILQ_REMOVE(&connections, con, connections);
+ free(con);
+ log(LL_DMN, "monitor closed from %s", source );
+ }
+ }
+ }
+
+ /* all connections gone? */
+
+ if (TAILQ_FIRST(&connections) == NULL)
+ accepted = 0;
+}
+
+/*---------------------------------------------------------------------------
+ * Try new incoming connection on the given socket.
+ * Setup client descriptor and send initial data.
+ *---------------------------------------------------------------------------*/
+void
+monitor_handle_connect(int sockfd, int is_local)
+{
+ struct monitor_connection *con;
+ struct monitor_rights *rp;
+
+#ifndef I4B_NOTCPIP_MONITOR
+ struct sockaddr_in ia;
+ u_int32_t ha = 0;
+#endif
+
+ struct sockaddr_un ua;
+ u_int8_t idata[I4B_MON_IDATA_SIZE];
+ int fd = -1, s, i, r_mask, t_events;
+ char source[FILENAME_MAX];
+
+ /* accept the connection */
+
+ if(is_local)
+ {
+ s = sizeof ua;
+ fd = accept(sockfd, (struct sockaddr *)&ua, &s);
+ strcpy(source, "local");
+
+#ifndef I4B_NOTCPIP_MONITOR
+ }
+ else
+ {
+ struct hostent *hp;
+
+ s = sizeof ia;
+ fd = accept(sockfd, (struct sockaddr *)&ia, &s);
+
+ hp = gethostbyaddr((char *)&ia.sin_addr, 4, AF_INET);
+
+ if(hp == NULL)
+ snprintf(source, sizeof source, "%s (%s)", inet_ntoa(ia.sin_addr), inet_ntoa(ia.sin_addr));
+ else
+ snprintf(source, sizeof source, "%s (%s)", hp->h_name, inet_ntoa(ia.sin_addr));
+
+ memcpy(&ha, &ia.sin_addr.s_addr, sizeof ha);
+
+ ha = ntohl(ha);
+#endif
+ }
+
+ /* check the access rights of this connection */
+
+ r_mask = 0;
+
+ for (rp = TAILQ_FIRST(&rights); rp != NULL; rp = TAILQ_NEXT(rp, list))
+ {
+ if(rp->local)
+ {
+ if(is_local)
+ {
+ r_mask = rp->rights;
+ break;
+ }
+
+#ifndef I4B_NOTCPIP_MONITOR
+ }
+ else
+ {
+ if((ha & rp->mask) == rp->net)
+ {
+ r_mask = rp->rights;
+ break;
+ }
+#endif
+ }
+ }
+
+ if(r_mask == 0)
+ {
+ /* no rights - go away */
+ log(LL_MER, "monitor access denied from %s", source);
+ close(fd);
+ return;
+ }
+
+ accepted = 1;
+
+ con = malloc(sizeof(struct monitor_connection));
+ memset(con, 0, sizeof *con);
+ TAILQ_INSERT_TAIL(&connections, con, connections);
+ con->sock = fd;
+ con->rights = r_mask;
+ strcpy(con->source, source);
+
+ log(LL_DMN, "monitor opened from %s rights 0x%x", source, r_mask);
+
+ /* send initial data */
+ I4B_PREP_CMD(idata, I4B_MON_IDATA_CODE);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMAJOR, MPROT_VERSION);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMINOR, MPROT_REL);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_NUMCTRL, ncontroller);
+ I4B_PUT_2B(idata, I4B_MON_IDATA_NUMENTR, nentries);
+ I4B_PUT_4B(idata, I4B_MON_IDATA_CLACCESS, r_mask);
+
+ if((sock_write(fd, idata, sizeof idata)) == -1)
+ {
+ log(LL_MER, "monitor_handle_connect: sock_write 1 error - %s", strerror(errno));
+ }
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ u_int8_t ictrl[I4B_MON_ICTRL_SIZE];
+
+ I4B_PREP_CMD(ictrl, I4B_MON_ICTRL_CODE);
+ I4B_PUT_STR(ictrl, I4B_MON_ICTRL_NAME, name_of_controller(isdn_ctrl_tab[i].ctrl_type, isdn_ctrl_tab[i].card_type));
+ I4B_PUT_2B(ictrl, I4B_MON_ICTRL_BUSID, 0);
+ I4B_PUT_4B(ictrl, I4B_MON_ICTRL_FLAGS, 0);
+ I4B_PUT_4B(ictrl, I4B_MON_ICTRL_NCHAN, 2);
+
+ if((sock_write(fd, ictrl, sizeof ictrl)) == -1)
+ {
+ log(LL_MER, "monitor_handle_connect: sock_write 2 error - %s", strerror(errno));
+ }
+
+ }
+
+ /* send device names from entries */
+
+ for(i=0; i < nentries; i++) /* walk thru all entries */
+ {
+ u_int8_t ictrl[I4B_MON_IDEV_SIZE];
+ cfg_entry_t *p;
+ char nbuf[64];
+ p = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ snprintf(nbuf, sizeof(nbuf), "%s%d ", bdrivername(p->usrdevicename), p->usrdeviceunit);
+
+ I4B_PREP_CMD(ictrl, I4B_MON_IDEV_CODE);
+/*XXX*/ I4B_PUT_2B(ictrl, I4B_MON_IDEV_STATE, 1);
+ I4B_PUT_STR(ictrl, I4B_MON_IDEV_NAME, nbuf);
+
+ if((sock_write(fd, ictrl, sizeof ictrl)) == -1)
+ {
+ log(LL_MER, "monitor_handle_connect: sock_write 3 error - %s", strerror(errno));
+ }
+ }
+
+/*XXX*/ t_events = con->events;
+/*XXX*/ con->events = -1;
+
+ /* current state of controller(s) */
+
+ for(i=0; i < ncontroller; i++)
+ {
+ monitor_evnt_tei(i, isdn_ctrl_tab[i].tei);
+ monitor_evnt_l12stat(i, LAYER_ONE, isdn_ctrl_tab[i].l1stat);
+ monitor_evnt_l12stat(i, LAYER_TWO, isdn_ctrl_tab[i].l2stat);
+ }
+
+ /* current state of entries */
+
+ for(i=0; i < nentries; i++)
+ {
+ cfg_entry_t *cep = &cfg_entry_tab[i];
+
+ if(cep->state == ST_CONNECTED)
+ {
+ monitor_evnt_connect(cep);
+ monitor_evnt_acct(cep);
+ monitor_evnt_charge(cep, cep->charge, 1);
+ }
+ }
+
+/*XXX*/ con->events = t_events;
+
+}
+
+/*---------------------------------------------------------------------------
+ * dump all monitor rights
+ *---------------------------------------------------------------------------*/
+static void
+cmd_dump_rights(int fd, int r_mask, u_int8_t *cmd, const char *source)
+{
+ struct monitor_rights * r;
+ int num_rights;
+ u_int8_t drini[I4B_MON_DRINI_SIZE];
+ u_int8_t dr[I4B_MON_DR_SIZE];
+
+ for (num_rights = 0, r = TAILQ_FIRST(&rights); r != NULL; r = TAILQ_NEXT(r, list))
+ num_rights++;
+
+ I4B_PREP_EVNT(drini, I4B_MON_DRINI_CODE);
+ I4B_PUT_2B(drini, I4B_MON_DRINI_COUNT, num_rights);
+
+ if((sock_write(fd, drini, sizeof drini)) == -1)
+ {
+ log(LL_MER, "cmd_dump_rights: sock_write 1 error - %s", strerror(errno));
+ }
+
+ for (r = TAILQ_FIRST(&rights); r != NULL; r = TAILQ_NEXT(r, list))
+ {
+ I4B_PREP_EVNT(dr, I4B_MON_DR_CODE);
+ I4B_PUT_4B(dr, I4B_MON_DR_RIGHTS, r->rights);
+ I4B_PUT_4B(dr, I4B_MON_DR_NET, r->net);
+ I4B_PUT_4B(dr, I4B_MON_DR_MASK, r->mask);
+ I4B_PUT_1B(dr, I4B_MON_DR_LOCAL, r->local);
+ if((sock_write(fd, dr, sizeof dr)) == -1)
+ {
+ log(LL_MER, "cmd_dump_rights: sock_write 2 error - %s", strerror(errno));
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * rescan config file
+ *---------------------------------------------------------------------------*/
+static void
+cmd_reread_cfg(int fd, int rights, u_int8_t *cmd, const char * source)
+{
+ rereadconfig(42);
+}
+
+/*---------------------------------------------------------------------------
+ * drop one connection
+ *---------------------------------------------------------------------------*/
+static void
+cmd_hangup(int fd, int rights, u_int8_t *cmd, const char * source)
+{
+ int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL);
+ int ctrl = I4B_GET_4B(cmd, I4B_MON_HANGUP_CTRL);
+
+ hangup_channel(ctrl, channel, source);
+}
+
+/*---------------------------------------------------------------------------
+ * dump all active monitor connections
+ *---------------------------------------------------------------------------*/
+static void
+cmd_dump_mcons(int fd, int rights, u_int8_t *cmd, const char * source)
+{
+ int num_connections;
+ struct monitor_connection *con;
+ u_int8_t dcini[I4B_MON_DCINI_SIZE];
+
+ for (num_connections = 0, con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ num_connections++;
+
+ I4B_PREP_EVNT(dcini, I4B_MON_DCINI_CODE);
+ I4B_PUT_2B(dcini, I4B_MON_DCINI_COUNT, num_connections);
+
+ if((sock_write(fd, dcini, sizeof dcini)) == -1)
+ {
+ log(LL_MER, "cmd_dump_mcons: sock_write 1 error - %s", strerror(errno));
+ }
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+#ifndef I4B_NOTCPIP_MONITOR
+ int namelen;
+ struct sockaddr_in name;
+#endif
+ u_int8_t dc[I4B_MON_DC_SIZE];
+
+ I4B_PREP_EVNT(dc, I4B_MON_DC_CODE);
+ I4B_PUT_4B(dc, I4B_MON_DC_RIGHTS, con->rights);
+
+#ifndef I4B_NOTCPIP_MONITOR
+ namelen = sizeof name;
+
+ if (getpeername(con->sock, (struct sockaddr*)&name, &namelen) == 0)
+ memcpy(dc+I4B_MON_DC_WHO, &name.sin_addr, sizeof name.sin_addr);
+#endif
+ if((sock_write(fd, dc, sizeof dc)) == -1)
+ {
+ log(LL_MER, "cmd_dump_mcons: sock_write 2 error - %s", strerror(errno));
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Handle a command from the given socket. The client
+ * has rights as specified in the rights parameter.
+ * Return non-zero if connection is closed.
+ *---------------------------------------------------------------------------*/
+static int
+monitor_command(struct monitor_connection * con, int fd, int rights)
+{
+ char cmd[I4B_MAX_MON_CLIENT_CMD];
+ u_int code;
+
+ /* command dispatch table */
+ typedef void (*cmd_func_t)(int fd, int rights, u_int8_t *cmd, const char *source);
+
+ static struct {
+ cmd_func_t call; /* function to execute */
+ u_int rights; /* necessary rights */
+ } cmd_tab[] =
+ {
+ /* 0 */ { NULL, 0 },
+ /* 1 */ { cmd_dump_rights, I4B_CA_COMMAND_FULL },
+ /* 2 */ { cmd_dump_mcons, I4B_CA_COMMAND_FULL },
+ /* 3 */ { cmd_reread_cfg, I4B_CA_COMMAND_FULL },
+ /* 4 */ { cmd_hangup, I4B_CA_COMMAND_FULL },
+ };
+#define NUMCMD (sizeof cmd_tab / sizeof cmd_tab[0])
+
+ u_long u;
+ int bytes;
+
+ /* Network transfer may deliver two or more packets concatenated.
+ * Peek at the header and read only one event at a time... */
+
+ ioctl(fd, FIONREAD, &u);
+
+ if (u < I4B_MON_CMD_HDR)
+ {
+ if (u == 0)
+ {
+ /* log(LL_MER, "monitor read 0 bytes"); */
+ /* socket closed by peer */
+ close(fd);
+ return 1;
+ }
+ return 0; /* not enough data there yet */
+ }
+
+ bytes = recv(fd, cmd, I4B_MON_CMD_HDR, MSG_PEEK);
+
+ if (bytes < I4B_MON_CMD_HDR)
+ {
+ log(LL_MER, "monitor read only %d bytes", bytes);
+ return 0; /* errh? something must be wrong... */
+ }
+
+ bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN);
+
+ if (bytes >= sizeof cmd)
+ {
+ close(fd);
+ log(LL_MER, "monitor: garbage on connection");
+ return 1;
+ }
+
+ /* now we know the size, it fits, so lets read it! */
+
+ if(sock_read(fd, cmd, bytes) <= 0)
+ {
+ log(LL_MER, "monitor: sock_read <= 0");
+ close(fd);
+ return 1;
+ }
+
+ /* decode command */
+ code = I4B_GET_2B(cmd, I4B_MON_CMD);
+
+ /* special case: may modify our connection descriptor, is
+ * beyound all rights checks */
+
+ if (code == I4B_MON_CCMD_SETMASK)
+ {
+/*XXX*/
+ /*
+ u_int major = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMAJOR);
+ u_int minor = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMINOR);
+ */
+
+ int events = I4B_GET_4B(cmd, I4B_MON_ICLIENT_EVENTS);
+ con->events = events & rights;
+ return 0;
+ }
+
+ if (code < 0 || code >= NUMCMD)
+ {
+ log(LL_MER, "illegal command from client, code = %d\n",
+ code);
+ return 0;
+ }
+
+ if (cmd_tab[code].call == NULL)
+ return 0;
+
+ if ((cmd_tab[code].rights & rights) == cmd_tab[code].rights)
+ cmd_tab[code].call(fd, rights, cmd, con->source);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * Check if somebody would receive an event with this mask.
+ * We are lazy and try to avoid assembling unneccesary packets.
+ * Return 0 if no one interested, nonzero otherwise.
+ *---------------------------------------------------------------------------*/
+static int
+anybody(int mask)
+{
+ struct monitor_connection * con;
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+ if ((con->events & mask) == mask)
+ return 1;
+ }
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * exec hangup command
+ *---------------------------------------------------------------------------*/
+static void
+hangup_channel(int controller, int channel, const char *source)
+{
+ cfg_entry_t * cep = NULL;
+ int i;
+
+ if(controller < ncontroller)
+ {
+ if(isdn_ctrl_tab[controller].state != CTRL_UP)
+ return;
+ for (i = 0; i < isdn_ctrl_tab[controller].nbch; i++)
+ {
+ if(isdn_ctrl_tab[controller].stateb[i] != CHAN_IDLE)
+ {
+ cep = get_cep_by_cc(controller, i);
+ if (cep != NULL && cep->isdnchannelused == channel &&
+ cep->isdncontrollerused == controller)
+ goto found;
+ }
+ }
+ }
+ /* not found */
+ return;
+
+found:
+ log(LL_CHD, "%05d %s manual disconnect (remote from %s)", cep->cdid, cep->name, source);
+ cep->hangup = 1;
+ return;
+}
+
+/*---------------------------------------------------------------------------
+ * Send an event to every connection interested in this kind of
+ * event
+ *---------------------------------------------------------------------------*/
+static void
+monitor_broadcast(int mask, u_int8_t *pkt, size_t bytes)
+{
+ struct monitor_connection *con;
+
+ for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections))
+ {
+ if ((con->events & mask) == mask)
+ {
+ int fd = con->sock;
+
+ if((sock_write(fd, pkt, bytes)) == -1)
+ {
+ log(LL_MER, "monitor_broadcast: sock_write error - %s", strerror(errno));
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+ * Post a logfile event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_log(int prio, const char * what, const char * msg)
+{
+ u_int8_t evnt[I4B_MON_LOGEVNT_SIZE];
+ time_t now;
+
+ if (!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_LOGEVNT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_PRIO, prio);
+ I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_WHAT, what);
+ I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_MSG, msg);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a charging event on the connection described
+ * by the given config entry.
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_charge(cfg_entry_t *cep, int units, int estimate)
+{
+ int mask;
+ time_t now;
+ u_int8_t evnt[I4B_MON_CHRG_SIZE];
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if(!anybody(mask))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_CHRG_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_CHANNEL, cep->isdnchannelused);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_UNITS, units);
+ I4B_PUT_4B(evnt, I4B_MON_CHRG_ESTIMATED, estimate ? 1 : 0);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a connection event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_connect(cfg_entry_t *cep)
+{
+ u_int8_t evnt[I4B_MON_CONNECT_SIZE];
+ char devname[I4B_MAX_MON_STRING];
+ int mask;
+ time_t now;
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if (!anybody(mask))
+ return;
+
+ time(&now);
+
+ snprintf(devname, sizeof devname, "%s%d", bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_CONNECT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_DIR, cep->direction == DIR_OUT ? 1 : 0);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_CONNECT_CHANNEL, cep->isdnchannelused);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_CFGNAME, cep->name);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_DEVNAME, devname);
+
+ if(cep->direction == DIR_OUT)
+ {
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->remote_phone_dialout.number);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->local_phone_dialout.number);
+ }
+ else
+ {
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->real_phone_incoming.number);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->local_phone_incoming.number);
+ }
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a disconnect event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_disconnect(cfg_entry_t *cep)
+{
+ u_int8_t evnt[I4B_MON_DISCONNECT_SIZE];
+ int mask;
+ time_t now;
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if (!anybody(mask))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_DISCONNECT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CHANNEL, cep->isdnchannelused);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post an up/down event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_updown(cfg_entry_t *cep, int up)
+{
+ u_int8_t evnt[I4B_MON_UPDOWN_SIZE];
+ int mask;
+ time_t now;
+
+ mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+
+ if (!anybody(mask))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_UPDOWN_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CHANNEL, cep->isdnchannelused);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_ISUP, up);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a Layer1/2 status change event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_l12stat(int controller, int layer, int state)
+{
+ u_int8_t evnt[I4B_MON_L12STAT_SIZE];
+ time_t now;
+
+ if(!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_L12STAT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_CTRL, controller);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_LAYER, layer);
+ I4B_PUT_4B(evnt, I4B_MON_L12STAT_STATE, state);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post a TEI change event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_tei(int controller, int tei)
+{
+ u_int8_t evnt[I4B_MON_TEI_SIZE];
+ time_t now;
+
+ if(!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_TEI_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_TEI_TSTAMP, (long)now);
+ I4B_PUT_4B(evnt, I4B_MON_TEI_CTRL, controller);
+ I4B_PUT_4B(evnt, I4B_MON_TEI_TEI, tei);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * Post an accounting event
+ *---------------------------------------------------------------------------*/
+void
+monitor_evnt_acct(cfg_entry_t *cep)
+{
+ u_int8_t evnt[I4B_MON_ACCT_SIZE];
+ time_t now;
+
+ if(!anybody(I4B_CA_EVNT_I4B))
+ return;
+
+ time(&now);
+
+ I4B_PREP_EVNT(evnt, I4B_MON_ACCT_CODE);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_TSTAMP, (long)now);
+
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_CTRL, cep->isdncontrollerused);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_CHAN, cep->isdnchannelused);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_OBYTES, cep->outbytes);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_OBPS, cep->outbps);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_IBYTES, cep->inbytes);
+ I4B_PUT_4B(evnt, I4B_MON_ACCT_IBPS, cep->inbps);
+
+ monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
+}
+
+/*---------------------------------------------------------------------------
+ * read from a socket
+ *---------------------------------------------------------------------------*/
+static ssize_t
+sock_read(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nread;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nread = read(fd, ptr, nleft)) < 0)
+ {
+ if(errno == EINTR)
+ {
+ nread = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+ else if(nread == 0)
+ {
+ break; /* EOF */
+ }
+
+ nleft -= nread;
+ ptr += nread;
+ }
+ return(nbytes - nleft);
+}
+
+/*---------------------------------------------------------------------------
+ * write to a socket
+ *---------------------------------------------------------------------------*/
+static ssize_t
+sock_write(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nwritten;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nwritten = write(fd, ptr, nleft)) <= 0)
+ {
+ if(errno == EINTR)
+ {
+ nwritten = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+ return(nbytes);
+}
+
+struct monitor_rights * monitor_next_rights(const struct monitor_rights *r)
+{
+ if (r == NULL)
+ return TAILQ_FIRST(&rights);
+ else
+ return TAILQ_NEXT(r, list);
+}
+
+#endif /* I4B_EXTERNAL_MONITOR */
diff --git a/usr.sbin/i4b/isdnd/msghdl.c b/usr.sbin/i4b/isdnd/msghdl.c
new file mode 100644
index 0000000..2687e48
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/msghdl.c
@@ -0,0 +1,1350 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - message from kernel handling routines
+ * --------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Aug 11 12:37:16 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_types.h>
+
+#if defined(__FreeBSD__)
+#include <net/if_var.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+/*---------------------------------------------------------------------------*
+ * handle incoming CONNECT_IND (=SETUP) message
+ *---------------------------------------------------------------------------*/
+void
+msg_connect_ind(msg_connect_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ char *src_tela = "ERROR-src_tela";
+ char *dst_tela = "ERROR-dst_tela";
+
+#define SRC (aliasing == 0 ? mp->src_telno : src_tela)
+#define DST (aliasing == 0 ? mp->dst_telno : dst_tela)
+
+ /* Add prefixes. All preexisting alias files are useless
+ if this is on. */
+ if(addprefix)
+ {
+ add_number_prefix(mp->src_telno, mp->src_ton);
+ add_number_prefix(mp->dst_telno, mp->dst_ton);
+ }
+
+ if(aliasing)
+ {
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+ }
+
+ if((cep = find_matching_entry_incoming(mp)) == NULL)
+ {
+ /* log message generated in find_matching_entry_incoming() */
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ handle_scrprs(mp->header.cdid, mp->scr_ind, mp->prs_ind, SRC);
+ return;
+ }
+
+ if(cep->cdid != CDID_UNUSED && cep->cdid != CDID_RESERVED)
+ {
+ /*
+ * This is an incoming call on a number we just dialed out.
+ * Stop our dial-out and accept the incoming call.
+ */
+ if(cep->saved_call.cdid != CDID_UNUSED &&
+ cep->saved_call.cdid != CDID_RESERVED)
+ {
+ int cdid;
+
+ /* disconnect old, not new */
+
+ cdid = cep->cdid;
+ cep->cdid = cep->saved_call.cdid;
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+ cep->cdid = cdid;
+
+ /*
+ * Shortcut the state machine and mark this
+ * entry as free
+ */
+/* XXX */ cep->state = ST_IDLE; /* this is an invalid */
+ /* transition, */
+ /* so no next_state() */
+ /* we have to wait here for an incoming */
+ /* disconnect message !!! (-hm) */
+ }
+ }
+
+ if(cep->inout == DIR_OUTONLY)
+ {
+ log(LL_CHD, "%05d %s incoming call from %s to %s not allowed by configuration!",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ handle_scrprs(mp->header.cdid, mp->scr_ind, mp->prs_ind, SRC);
+ return;
+ }
+
+ cep->charge = 0;
+ cep->last_charge = 0;
+
+ switch(cep->dialin_reaction)
+ {
+ case REACT_ACCEPT:
+ log(LL_CHD, "%05d %s accepting: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ decr_free_channels(mp->controller);
+ next_state(cep, EV_MCI);
+ break;
+
+ case REACT_REJECT:
+ log(LL_CHD, "%05d %s rejecting: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(cep, mp->header.cdid, SETUP_RESP_REJECT,
+ (CAUSET_I4B << 8) | CAUSE_I4B_REJECT);
+ cep->cdid = CDID_UNUSED;
+ break;
+
+ case REACT_IGNORE:
+ log(LL_CHD, "%05d %s ignoring: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ cep->cdid = CDID_UNUSED;
+ break;
+
+ case REACT_ANSWER:
+ decr_free_channels(mp->controller);
+ if(cep->alert)
+ {
+ if(mp->display)
+ {
+ log(LL_CHD, "%05d %s alerting: incoming call from %s to %s (%s)",
+ mp->header.cdid, cep->name, SRC, DST, mp->display);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s alerting: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ }
+ next_state(cep, EV_ALRT);
+ }
+ else
+ {
+ if(mp->display)
+ {
+ log(LL_CHD, "%05d %s answering: incoming call from %s to %s (%s)",
+ mp->header.cdid, cep->name, SRC, DST, mp->display);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s answering: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ }
+ next_state(cep, EV_MCI);
+ }
+ break;
+
+ case REACT_CALLBACK:
+
+#ifdef NOTDEF
+/*XXX reserve channel ??? */ decr_free_channels(mp->controller);
+#endif
+ if(cep->cdid == CDID_RESERVED)
+ {
+ log(LL_CHD, "%05d %s reserved: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ sendm_connect_resp(cep, mp->header.cdid, SETUP_RESP_REJECT,
+#if 0
+ (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+#else
+ (CAUSET_I4B << 8) | CAUSE_I4B_REJECT);
+#endif
+ /* no state change */
+ }
+ else
+ {
+ sendm_connect_resp(cep, mp->header.cdid, SETUP_RESP_REJECT,
+#if 0
+ (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+#else
+ (CAUSET_I4B << 8) | CAUSE_I4B_REJECT);
+#endif
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ {
+ cep->budget_callback_req++;
+ cep->budget_calltype = 0;
+ if(cep->budget_callbackncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget: call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+ cep->cdid = CDID_UNUSED;
+ cep->budget_callback_rej++;
+ break;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_CBACK;
+ }
+ }
+
+ log(LL_CHD, "%05d %s callback: incoming call from %s to %s",
+ mp->header.cdid, cep->name, SRC, DST);
+
+ cep->last_release_time = time(NULL);
+ cep->cdid = CDID_RESERVED;
+ next_state(cep, EV_CBRQ);
+ }
+ break;
+
+ default:
+ log(LL_WRN, "msg_connect_ind: unknown response type, tx SETUP_RESP_DNTCRE");
+ sendm_connect_resp(NULL, mp->header.cdid, SETUP_RESP_DNTCRE, 0);
+ break;
+ }
+ handle_scrprs(mp->header.cdid, mp->scr_ind, mp->prs_ind, SRC);
+#undef SRC
+#undef DST
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming CONNECT_ACTIVE_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_connect_active_ind(msg_connect_active_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ char *device;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_connect_active_ind: cdid not found!");
+ return;
+ }
+
+ cep->isdncontrollerused = mp->controller;
+ cep->isdnchannelused = mp->channel;
+
+ cep->aoc_now = cep->connect_time = time(NULL);
+ cep->aoc_last = 0;
+ cep->aoc_diff = 0;
+ cep->aoc_valid = AOC_INVALID;
+
+ cep->local_disconnect = DISCON_REM;
+
+ cep->inbytes = INVALID;
+ cep->outbytes = INVALID;
+ cep->hangup = 0;
+
+ device = bdrivername(cep->usrdevicename);
+
+ /* set the B-channel to active */
+
+ if((set_channel_busy(cep->isdncontrollerused, cep->isdnchannelused)) == ERROR)
+ log(LL_ERR, "msg_connect_active_ind: set_channel_busy failed!");
+
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s outgoing call active (ctl %d, ch %d, %s%d)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused,
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+
+ if(cep->budget_calltype)
+ {
+ if(cep->budget_calltype == BUDGET_TYPE_CBACK)
+ {
+ cep->budget_callback_done++;
+ cep->budget_callbackncalls_cnt--;
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cback-budget = %d",
+ cep->name, cep->budget_callbackncalls_cnt)));
+ if(cep->budget_callbacks_file != NULL)
+ upd_callstat_file(cep->budget_callbacks_file, cep->budget_callbacksfile_rotate);
+ }
+ else if(cep->budget_calltype == BUDGET_TYPE_COUT)
+ {
+ cep->budget_callout_done++;
+ cep->budget_calloutncalls_cnt--;
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cout-budget = %d",
+ cep->name, cep->budget_calloutncalls_cnt)));
+ if(cep->budget_callouts_file != NULL)
+ upd_callstat_file(cep->budget_callouts_file, cep->budget_calloutsfile_rotate);
+ }
+ cep->budget_calltype = 0;
+ }
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s incoming call active (ctl %d, ch %d, %s%d)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused,
+ bdrivername(cep->usrdevicename), cep->usrdeviceunit);
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_connect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_connect(cep);
+#endif
+
+ if(isdntime && (mp->datetime[0] != '\0'))
+ {
+ log(LL_DMN, "date/time from exchange = %s", mp->datetime);
+ }
+
+ next_state(cep, EV_MCAI);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming PROCEEDING_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_proceeding_ind(msg_proceeding_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_proceeding_ind: cdid not found!");
+ return;
+ }
+
+ cep->isdncontrollerused = mp->controller;
+ cep->isdnchannelused = mp->channel;
+
+ /* set the B-channels active */
+
+ if((set_channel_busy(cep->isdncontrollerused, cep->isdnchannelused)) == ERROR)
+ log(LL_ERR, "msg_proceeding_ind: set_channel_busy failed!");
+
+ log(LL_CHD, "%05d %s outgoing call proceeding (ctl %d, ch %d)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming ALERT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_alert_ind(msg_alert_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_alert_ind: cdid not found!");
+ return;
+ }
+#ifdef NOTDEF
+ log(LL_CHD, "%05d %s incoming alert", cep->cdid, cep->name);
+#endif
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming L12STAT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_l12stat_ind(msg_l12stat_ind_t *ml)
+{
+ if((ml->controller < 0) || (ml->controller >= ncontroller))
+ {
+ log(LL_ERR, "msg_l12stat_ind: invalid controller number [%d]!", ml->controller);
+ return;
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_l12stat(ml->controller, ml->layer, ml->state);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_l12stat(ml->controller, ml->layer, ml->state);
+#endif
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_l12stat_ind: unit %d, layer %d, state %d",
+ ml->controller, ml->layer, ml->state)));
+
+ if(ml->layer == LAYER_ONE)
+ {
+ if(ml->state == LAYER_IDLE)
+ isdn_ctrl_tab[ml->controller].l2stat = ml->state;
+ isdn_ctrl_tab[ml->controller].l1stat = ml->state;
+ }
+ else if(ml->layer == LAYER_TWO)
+ {
+ if(ml->state == LAYER_ACTIVE)
+ isdn_ctrl_tab[ml->controller].l1stat = ml->state;
+ isdn_ctrl_tab[ml->controller].l2stat = ml->state;
+ }
+ else
+ {
+ log(LL_ERR, "msg_l12stat_ind: invalid layer number [%d]!", ml->layer);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming TEIASG_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_teiasg_ind(msg_teiasg_ind_t *mt)
+{
+ if((mt->controller < 0) || (mt->controller >= ncontroller))
+ {
+ log(LL_ERR, "msg_teiasg_ind: invalid controller number [%d]!", mt->controller);
+ return;
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_tei(mt->controller, mt->tei);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_tei(mt->controller, mt->tei);
+#endif
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_teiasg_ind: unit %d, tei = %d",
+ mt->controller, mt->tei)));
+
+ isdn_ctrl_tab[mt->controller].tei = mt->tei;
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming PDEACT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_pdeact_ind(msg_pdeact_ind_t *md)
+{
+ int i;
+ int ctrl = md->controller;
+ cfg_entry_t *cep;
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ {
+ display_l12stat(ctrl, LAYER_ONE, LAYER_IDLE);
+ display_l12stat(ctrl, LAYER_TWO, LAYER_IDLE);
+ display_tei(ctrl, -1);
+ }
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ {
+ monitor_evnt_l12stat(ctrl, LAYER_ONE, LAYER_IDLE);
+ monitor_evnt_l12stat(ctrl, LAYER_TWO, LAYER_IDLE);
+ monitor_evnt_tei(ctrl, -1);
+ }
+#endif
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_pdeact_ind: unit %d, persistent deactivation", ctrl)));
+
+ isdn_ctrl_tab[ctrl].l1stat = LAYER_IDLE;
+ isdn_ctrl_tab[ctrl].l2stat = LAYER_IDLE;
+ isdn_ctrl_tab[ctrl].tei = -1;
+
+ for(i=0; i < nentries; i++)
+ {
+ if((cfg_entry_tab[i].cdid != CDID_UNUSED) &&
+ (cfg_entry_tab[i].isdncontrollerused == ctrl))
+ {
+ cep = &cfg_entry_tab[i];
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ continue;
+ }
+
+ cep->cdid = CDID_UNUSED;
+
+ cep->last_release_time = time(NULL);
+
+ SET_CAUSE_TYPE(cep->disc_cause, CAUSET_I4B);
+ SET_CAUSE_VAL(cep->disc_cause, CAUSE_I4B_L1ERROR);
+
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s outgoing call disconnected (local)",
+ cep->cdid, cep->name);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s incoming call disconnected (local)",
+ cep->cdid, cep->name);
+ }
+
+ log(LL_CHD, "%05d %s cause %s",
+ cep->cdid, cep->name, print_i4b_cause(cep->disc_cause));
+
+#ifdef USE_CURSES
+ if(do_fullscreen && (cep->connect_time > 0))
+ display_disconnect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_disconnect(cep);
+#endif
+
+ if(cep->disconnectprog)
+ exec_connect_prog(cep, cep->disconnectprog, 1);
+
+ if(cep->connect_time > 0)
+ {
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s charging: %d units, %d seconds",
+ cep->cdid, cep->name, cep->charge,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s connected %d seconds",
+ cep->cdid, cep->name,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+
+ if((cep->inbytes != INVALID) && (cep->outbytes != INVALID))
+ {
+ if((cep->ioutbytes != cep->outbytes) ||
+ (cep->iinbytes != cep->inbytes))
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d (in %d, out %d)",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes,
+ cep->iinbytes, cep->ioutbytes);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes);
+ }
+ }
+ }
+
+ if(useacctfile && (cep->connect_time > 0))
+ {
+ int con_secs;
+ char logdatetime[41];
+ struct tm *tp;
+
+ con_secs = difftime(time(NULL), cep->connect_time);
+
+ tp = localtime(&cep->connect_time);
+
+ strftime(logdatetime,40,I4B_TIME_FORMAT,tp);
+
+ if(cep->inbytes != INVALID && cep->outbytes != INVALID)
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d) (%d/%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs,
+ cep->inbytes, cep->outbytes);
+ }
+ else
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs);
+ }
+ }
+
+ /* set the B-channel inactive */
+
+ if((set_channel_idle(cep->isdncontrollerused, cep->isdnchannelused)) == ERROR)
+ log(LL_ERR, "msg_pdeact_ind: set_channel_idle failed!");
+
+ incr_free_channels(cep->isdncontrollerused);
+
+ cep->connect_time = 0;
+
+ cep->state = ST_IDLE;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming NEGCOMP_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_negcomplete_ind(msg_negcomplete_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_negcomp_ind: cdid not found");
+ return;
+ }
+
+ if(cep->connectprog)
+ exec_connect_prog(cep, cep->connectprog, 0);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming IFSTATE_CHANGED indication
+ *---------------------------------------------------------------------------*/
+void
+msg_ifstatechg_ind(msg_ifstatechg_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ char *device;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_negcomp_ind: cdid not found");
+ return;
+ }
+
+ device = bdrivername(cep->usrdevicename);
+ log(LL_DBG, "%s%d: switched to state %d", device, cep->usrdeviceunit, mp->state);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DISCONNECT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_disconnect_ind(msg_disconnect_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_disconnect_ind: cdid not found");
+ return;
+ }
+
+ /* is this an aborted out-call prematurely called back? */
+ if (cep->saved_call.cdid == mp->header.cdid)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "aborted outcall %05d disconnected",
+ mp->header.cdid)));
+ cep->saved_call.cdid = CDID_UNUSED;
+
+ set_channel_idle(cep->saved_call.controller, cep->saved_call.channel);
+
+ incr_free_channels(cep->saved_call.controller);
+ return;
+ }
+
+ cep->last_release_time = time(NULL);
+ cep->disc_cause = mp->cause;
+
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s outgoing call disconnected %s",
+ cep->cdid, cep->name,
+ cep->local_disconnect == DISCON_LOC ?
+ "(local)" : "(remote)");
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s incoming call disconnected %s",
+ cep->cdid, cep->name,
+ cep->local_disconnect == DISCON_LOC ?
+ "(local)" : "(remote)");
+ }
+
+ log(LL_CHD, "%05d %s cause %s",
+ cep->cdid, cep->name, print_i4b_cause(mp->cause));
+
+#ifdef USE_CURSES
+ if(do_fullscreen && (cep->connect_time > 0))
+ display_disconnect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_disconnect(cep);
+#endif
+
+ if(cep->disconnectprog)
+ exec_connect_prog(cep, cep->disconnectprog, 1);
+
+ if(cep->connect_time > 0)
+ {
+ if(cep->direction == DIR_OUT)
+ {
+ log(LL_CHD, "%05d %s charging: %d units, %d seconds",
+ cep->cdid, cep->name, cep->charge,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s connected %d seconds",
+ cep->cdid, cep->name,
+ (int)difftime(time(NULL), cep->connect_time));
+ }
+
+ if((cep->inbytes != INVALID) && (cep->outbytes != INVALID))
+ {
+ if((cep->ioutbytes != cep->outbytes) ||
+ (cep->iinbytes != cep->inbytes))
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d (in %d, out %d)",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes,
+ cep->iinbytes, cep->ioutbytes);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s accounting: in %d, out %d",
+ cep->cdid, cep->name,
+ cep->inbytes, cep->outbytes);
+ }
+ }
+ }
+
+ if(useacctfile && (cep->connect_time > 0))
+ {
+ int con_secs;
+ char logdatetime[41];
+ struct tm *tp;
+
+ con_secs = difftime(time(NULL), cep->connect_time);
+
+ tp = localtime(&cep->connect_time);
+
+ strftime(logdatetime,40,I4B_TIME_FORMAT,tp);
+
+ if(cep->inbytes != INVALID && cep->outbytes != INVALID)
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d) (%d/%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs,
+ cep->inbytes, cep->outbytes);
+ }
+ else
+ {
+ fprintf(acctfp, "%s - %s %s %d (%d)\n",
+ logdatetime, getlogdatetime(),
+ cep->name, cep->charge, con_secs);
+ }
+ }
+
+ /* set the B-channel inactive */
+
+ set_channel_idle(cep->isdncontrollerused, cep->isdnchannelused);
+
+ incr_free_channels(cep->isdncontrollerused);
+
+ cep->connect_time = 0;
+ cep->cdid = CDID_UNUSED;
+
+ next_state(cep, EV_MDI);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DIALOUT message
+ *---------------------------------------------------------------------------*/
+void
+msg_dialout(msg_dialout_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialout: dial req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = find_by_device_for_dialout(mp->driver, mp->driver_unit)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialout: config entry reserved or no match")));
+ return;
+ }
+
+ if(cep->inout == DIR_INONLY)
+ {
+ dialresponse(cep, DSTAT_INONLY);
+ return;
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ cep->budget_calltype = 0;
+ cep->budget_callout_req++;
+
+ if(cep->budget_calloutncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget for calling out", 0, cep->name);
+ cep->budget_callout_rej++;
+ dialresponse(cep, DSTAT_TFAIL);
+ return;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_COUT;
+ }
+ }
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialout: get_cdid() returned 0!")));
+ return;
+ }
+
+ cep->charge = 0;
+ cep->last_charge = 0;
+ cep->hangup = 0;
+
+ next_state(cep, EV_MDO);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DIALOUTNUMBER message
+ *---------------------------------------------------------------------------*/
+void
+msg_dialoutnumber(msg_dialoutnumber_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialoutnumber: dial req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = find_by_device_for_dialoutnumber(mp)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialoutnumber: config entry reserved or no match")));
+ return;
+ }
+
+ if(cep->inout == DIR_INONLY)
+ {
+ dialresponse(cep, DSTAT_INONLY);
+ return;
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ cep->budget_calltype = 0;
+ cep->budget_callout_req++;
+
+ if(cep->budget_calloutncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget for calling out", 0, cep->name);
+ cep->budget_callout_rej++;
+ dialresponse(cep, DSTAT_TFAIL);
+ return;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_COUT;
+ }
+ }
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialoutnumber: get_cdid() returned 0!")));
+ return;
+ }
+
+ cep->keypad[0] = '\0';
+ cep->charge = 0;
+ cep->last_charge = 0;
+ cep->hangup = 0;
+
+ next_state(cep, EV_MDO);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming KEYPAD message
+ *---------------------------------------------------------------------------*/
+void
+msg_keypad(msg_keypad_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_keypad: dial req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = find_by_device_for_keypad(mp->driver, mp->driver_unit, mp->cmdlen, mp->cmd)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_keypad: config entry reserved or no match")));
+ return;
+ }
+
+ if(cep->inout == DIR_INONLY)
+ {
+ dialresponse(cep, DSTAT_INONLY);
+ return;
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ cep->budget_calltype = 0;
+ cep->budget_callout_req++;
+
+ if(cep->budget_calloutncalls_cnt == 0)
+ {
+ log(LL_CHD, "%05d %s no budget for calling out", 0, cep->name);
+ cep->budget_callout_rej++;
+ dialresponse(cep, DSTAT_TFAIL);
+ return;
+ }
+ else
+ {
+ cep->budget_calltype = BUDGET_TYPE_COUT;
+ }
+ }
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_keypad: get_cdid() returned 0!")));
+ return;
+ }
+
+ cep->charge = 0;
+ cep->last_charge = 0;
+ cep->hangup = 0;
+
+ next_state(cep, EV_MDO);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming DRVRDISC_REQ message
+ *---------------------------------------------------------------------------*/
+void
+msg_drvrdisc_req(msg_drvrdisc_req_t *mp)
+{
+ cfg_entry_t *cep;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_drvrdisc_req: req from %s, unit %d", bdrivername(mp->driver), mp->driver_unit)));
+
+ if((cep = get_cep_by_driver(mp->driver, mp->driver_unit)) == NULL)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_drvrdisc_req: config entry not found")));
+ return;
+ }
+ next_state(cep, EV_DRQ);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming ACCOUNTING message
+ *---------------------------------------------------------------------------*/
+void
+msg_accounting(msg_accounting_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = find_active_entry_by_driver(mp->driver, mp->driver_unit)) == NULL)
+ {
+ log(LL_WRN, "msg_accounting: no config entry found!");
+ return;
+ }
+
+ cep->inbytes = mp->inbytes;
+ cep->iinbytes = mp->iinbytes;
+ cep->outbytes = mp->outbytes;
+ cep->ioutbytes = mp->ioutbytes;
+ cep->inbps = mp->inbps;
+ cep->outbps = mp->outbps;
+
+ if(mp->accttype == ACCT_DURING)
+ {
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_acct(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_acct(cep);
+#endif
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming CHARGING message
+ *---------------------------------------------------------------------------*/
+void
+msg_charging_ind(msg_charging_ind_t *mp)
+{
+ static char *cttab[] = {
+ "invalid",
+ "AOCD",
+ "AOCE",
+ "estimated" };
+
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_charging_ind: cdid not found");
+ return;
+ }
+
+ if(mp->units_type < CHARGE_INVALID || mp->units_type > CHARGE_CALC)
+ {
+ log(LL_ERR, "msg_charging: units_type %d out of range!", mp->units_type);
+ error_exit(1, "msg_charging: units_type %d out of range!", mp->units_type);
+ }
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_charging: %d unit(s) (%s)",
+ mp->units, cttab[mp->units_type])));
+
+ cep->charge = mp->units;
+
+ switch(mp->units_type)
+ {
+ case CHARGE_AOCD:
+ if((cep->unitlengthsrc == ULSRC_DYN) &&
+ (cep->charge != cep->last_charge))
+ {
+ cep->last_charge = cep->charge;
+ handle_charge(cep);
+ }
+ break;
+
+ case CHARGE_CALC:
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_ccharge(cep, mp->units);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_charge(cep, mp->units, 1);
+#endif
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming IDLE_TIMEOUT_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_idle_timeout_ind(msg_idle_timeout_ind_t *mp)
+{
+ cfg_entry_t *cep;
+
+ if((cep = get_cep_by_cdid(mp->header.cdid)) == NULL)
+ {
+ log(LL_WRN, "msg_idle_timeout_ind: cdid not found!");
+ return;
+ }
+
+ cep->local_disconnect = DISCON_LOC;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_idle_timeout_ind: idletimeout, kernel sent disconnect!")));
+
+ check_and_kill(cep);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming MSG_PACKET_IND message
+ *---------------------------------------------------------------------------*/
+static char *
+strapp(char *buf, const char *txt)
+{
+ while(*txt)
+ *buf++ = *txt++;
+ *buf = '\0';
+ return buf;
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming MSG_PACKET_IND message
+ *---------------------------------------------------------------------------*/
+static char *
+ipapp(char *buf, unsigned long a )
+{
+ unsigned long ma = ntohl( a );
+
+ buf += sprintf(buf, "%lu.%lu.%lu.%lu",
+ (ma>>24)&0xFF,
+ (ma>>16)&0xFF,
+ (ma>>8)&0xFF,
+ (ma)&0xFF);
+ return buf;
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming MSG_PACKET_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_packet_ind(msg_packet_ind_t *mp)
+{
+ cfg_entry_t *cep;
+ struct ip *ip;
+ u_char *proto_hdr;
+ char tmp[80];
+ char *cptr = tmp;
+ char *name = "???";
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(cep->usrdevicename == mp->driver &&
+ cep->usrdeviceunit == mp->driver_unit)
+ {
+ name = cep->name;
+ break;
+ }
+ }
+
+ ip = (struct ip*)mp->pktdata;
+ proto_hdr = mp->pktdata + ((ip->ip_hl)<<2);
+
+ if( ip->ip_p == IPPROTO_TCP )
+ {
+ struct tcphdr* tcp = (struct tcphdr*)proto_hdr;
+
+ cptr = strapp( cptr, "TCP " );
+ cptr = ipapp( cptr, ip->ip_src.s_addr );
+ cptr += sprintf( cptr, ":%u -> ", ntohs( tcp->th_sport ) );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr );
+ cptr += sprintf( cptr, ":%u", ntohs( tcp->th_dport ) );
+
+ if(tcp->th_flags & TH_FIN) cptr = strapp( cptr, " FIN" );
+ if(tcp->th_flags & TH_SYN) cptr = strapp( cptr, " SYN" );
+ if(tcp->th_flags & TH_RST) cptr = strapp( cptr, " RST" );
+ if(tcp->th_flags & TH_PUSH) cptr = strapp( cptr, " PUSH" );
+ if(tcp->th_flags & TH_ACK) cptr = strapp( cptr, " ACK" );
+ if(tcp->th_flags & TH_URG) cptr = strapp( cptr, " URG" );
+ }
+ else if( ip->ip_p == IPPROTO_UDP )
+ {
+ struct udphdr* udp = (struct udphdr*)proto_hdr;
+
+ cptr = strapp( cptr, "UDP " );
+ cptr = ipapp( cptr, ip->ip_src.s_addr );
+ cptr += sprintf( cptr, ":%u -> ", ntohs( udp->uh_sport ) );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr );
+ cptr += sprintf( cptr, ":%u", ntohs( udp->uh_dport ) );
+ }
+ else if( ip->ip_p == IPPROTO_ICMP )
+ {
+ struct icmp* icmp = (struct icmp*)proto_hdr;
+
+ cptr += sprintf( cptr, "ICMP:%u.%u", icmp->icmp_type, icmp->icmp_code);
+ cptr = ipapp( cptr, ip->ip_src.s_addr );
+ cptr = strapp( cptr, " -> " );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr );
+ }
+ else
+ {
+ cptr += sprintf( cptr, "PROTO=%u ", ip->ip_p);
+ cptr = ipapp( cptr, ip->ip_src.s_addr);
+ cptr = strapp( cptr, " -> " );
+ cptr = ipapp( cptr, ip->ip_dst.s_addr);
+ }
+
+ log(LL_PKT, "%s %s %u %s",
+ name, mp->direction ? "send" : "recv",
+ ntohs( ip->ip_len ), tmp );
+}
+
+/*---------------------------------------------------------------------------*
+ * get a cdid from kernel
+ *---------------------------------------------------------------------------*/
+int
+get_cdid(void)
+{
+ msg_cdid_req_t mcr;
+
+ mcr.cdid = 0;
+
+ if((ioctl(isdnfd, I4B_CDID_REQ, &mcr)) < 0)
+ {
+ log(LL_ERR, "get_cdid: ioctl I4B_CDID_REQ failed: %s", strerror(errno));
+ error_exit(1, "get_cdid: ioctl I4B_CDID_REQ failed: %s", strerror(errno));
+ }
+
+ return(mcr.cdid);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "connect request" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_connect_req(cfg_entry_t *cep)
+{
+ msg_connect_req_t mcr;
+ int ret;
+
+ cep->local_disconnect = DISCON_REM;
+
+ cep->unitlength = get_current_rate(cep, 1);
+
+ mcr.cdid = cep->cdid;
+
+ mcr.controller = cep->isdncontrollerused;
+ mcr.channel = cep->isdnchannelused;
+ mcr.txdelay = cep->isdntxdelout;
+
+ mcr.bprot = cep->b1protocol;
+ mcr.bcap = cep->bcap;
+
+ mcr.driver = cep->usrdevicename;
+ mcr.driver_unit = cep->usrdeviceunit;
+
+ /* setup the shorthold data */
+ mcr.shorthold_data.shorthold_algorithm = cep->shorthold_algorithm;
+ mcr.shorthold_data.unitlen_time = cep->unitlength;
+ mcr.shorthold_data.idle_time = cep->idle_time_out;
+ mcr.shorthold_data.earlyhup_time = cep->earlyhangup;
+
+ if(cep->unitlengthsrc == ULSRC_DYN)
+ mcr.unitlen_method = ULEN_METHOD_DYNAMIC;
+ else
+ mcr.unitlen_method = ULEN_METHOD_STATIC;
+
+ strcpy(mcr.dst_telno, cep->remote_phone_dialout.number);
+ if(cep->usesubaddr)
+ strcpy(mcr.dst_subaddr, cep->remote_phone_dialout.subaddr);
+ strcpy(mcr.src_telno, cep->local_phone_dialout.number);
+ if(cep->usesubaddr)
+ strcpy(mcr.src_subaddr, cep->local_phone_dialout.subaddr);
+ strcpy(mcr.keypad, cep->keypad);
+
+ cep->last_dial_time = time(NULL);
+ cep->direction = DIR_OUT;
+
+ DBGL(DL_CNST, (log(LL_DBG, "sendm_connect_req: ctrl = %d, chan = %d", cep->isdncontrollerused, cep->isdnchannelused)));
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_REQ, &mcr)) < 0)
+ {
+ log(LL_ERR, "sendm_connect_req: ioctl I4B_CONNECT_REQ failed: %s", strerror(errno));
+ error_exit(1, "sendm_connect_req: ioctl I4B_CONNECT_REQ failed: %s", strerror(errno));
+ }
+
+ decr_free_channels(cep->isdncontrollerused);
+
+ log(LL_CHD, "%05d %s dialing out from %s to %s",
+ cep->cdid,
+ cep->name,
+ aliasing ? get_alias(cep->local_phone_dialout.number) : cep->local_phone_dialout.number,
+ aliasing ? get_alias(cep->remote_phone_dialout.number) : cep->remote_phone_dialout.number);
+
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "connect response" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_connect_resp(cfg_entry_t *cep, int cdid, int response, cause_t cause)
+{
+ msg_connect_resp_t mcr;
+ int ret;
+
+ mcr.cdid = cdid;
+
+ mcr.response = response;
+
+ if(response == SETUP_RESP_REJECT)
+ {
+ mcr.cause = cause;
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_connect_resp: reject, cause=0x%x", cause)));
+ }
+ else if(response == SETUP_RESP_ACCEPT)
+ {
+ cep->direction = DIR_IN;
+
+ mcr.txdelay = cep->isdntxdelin;
+
+ mcr.bprot = cep->b1protocol;
+ mcr.bcap = cep->bcap;
+
+ mcr.driver = cep->usrdevicename;
+ mcr.driver_unit = cep->usrdeviceunit;
+
+ mcr.max_idle_time = cep->idle_time_in;
+
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_connect_resp: accept")));
+ }
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &mcr)) < 0)
+ {
+ log(LL_ERR, "sendm_connect_resp: ioctl I4B_CONNECT_RESP failed: %s", strerror(errno));
+ error_exit(1, "sendm_connect_resp: ioctl I4B_CONNECT_RESP failed: %s", strerror(errno));
+ }
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "disconnect request" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_disconnect_req(cfg_entry_t *cep, cause_t cause)
+{
+ msg_discon_req_t mcr;
+ int ret = 0;
+
+ mcr.cdid = cep->cdid;
+
+ mcr.cause = cause;
+
+ cep->local_disconnect = DISCON_LOC;
+
+ if((ret = ioctl(isdnfd, I4B_DISCONNECT_REQ, &mcr)) < 0)
+ {
+ log(LL_ERR, "sendm_disconnect_req: ioctl I4B_DISCONNECT_REQ failed: %s", strerror(errno));
+ }
+ else
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_disconnect_req: sent DISCONNECT_REQ")));
+ }
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "alert request" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_alert_req(cfg_entry_t *cep)
+{
+ msg_alert_req_t mar;
+ int ret;
+
+ mar.cdid = cep->cdid;
+
+ if((ret = ioctl(isdnfd, I4B_ALERT_REQ, &mar)) < 0)
+ {
+ log(LL_ERR, "sendm_alert_req: ioctl I4B_ALERT_REQ failed: %s", strerror(errno));
+ error_exit(1, "sendm_alert_req: ioctl I4B_ALERT_REQ failed: %s", strerror(errno));
+ }
+ else
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_alert_req: sent ALERT_REQ")));
+ }
+ return(ret);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/pathnames.h b/usr.sbin/i4b/isdnd/pathnames.h
new file mode 100644
index 0000000..44f9c6d
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/pathnames.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - location of files
+ * ------------------------------
+ *
+ * $Id: pathnames.h,v 1.11 2000/10/09 11:17:07 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Oct 2 22:55:28 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _PATHNAMES_H_
+#define _PATHNAMES_H_
+
+#define I4BDEVICE "/dev/i4b"
+
+#define ETCPATH "/etc/isdn"
+
+#define CONFIG_FILE_DEF "/etc/isdn/isdnd.rc"
+
+#define RATES_FILE_DEF "/etc/isdn/isdnd.rates"
+
+#define HOLIDAY_FILE_DEF "/etc/isdn/holidays"
+
+#define TINA_FILE_DEF "/etc/isdn/tinainitprog"
+
+#define LOG_FILE_DEF "/var/log/isdnd.log"
+
+#define ACCT_FILE_DEF "/var/log/isdnd.acct"
+
+#define PIDFILE "/var/run/isdnd.pid"
+
+#endif /* _PATHNAMES_H_ */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/pcause.c b/usr.sbin/i4b/isdnd/pcause.c
new file mode 100644
index 0000000..99aa089
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/pcause.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * printing cause values
+ * ---------------------
+ *
+ * $Id: pcause.c,v 1.13 2000/10/09 12:53:29 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:48:07 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static char *cause_i4b_tab[CAUSE_I4B_MAX+1];
+static char *cause_q850_tab[CAUSE_Q850_MAX];
+
+char *
+print_i4b_cause(cause_t code)
+{
+ static char error_message[128];
+
+ snprintf(error_message, sizeof(error_message), "%d: ", GET_CAUSE_VAL(code));
+
+ switch(GET_CAUSE_TYPE(code))
+ {
+ case CAUSET_Q850:
+ strcat(error_message, cause_q850_tab[GET_CAUSE_VAL(code)]);
+ strcat(error_message, " (Q.850)");
+ break;
+
+ case CAUSET_I4B:
+ if((GET_CAUSE_VAL(code) < CAUSE_I4B_NORMAL) ||
+ (GET_CAUSE_VAL(code) >= CAUSE_I4B_MAX))
+ {
+ SET_CAUSE_VAL(code, CAUSE_I4B_MAX);
+ }
+ strcat(error_message, cause_i4b_tab[GET_CAUSE_VAL(code)]);
+ strcat(error_message, " (I4B)");
+ break;
+
+ default:
+ strcat(error_message, "ERROR: unknown cause type!");
+ break;
+ }
+ return(error_message);
+}
+
+static char *cause_i4b_tab[CAUSE_I4B_MAX+1] = {
+ "normal call clearing",
+ "user busy",
+ "channel not available",
+ "incompatible source or destination",
+ "call rejected",
+ "destination out of order",
+ "temporary failure",
+ "layer 1 error / persistent deactivation",
+ "dialing impossible on leased line",
+ "ERROR, invalid I4B cause value!"
+};
+
+static char *cause_q850_tab[CAUSE_Q850_MAX] = {
+ "Normal D-channel shutdown",
+ "Unallocated (unassigned) number",
+ "No route to specified transit network (national use)",
+ "No route to destination",
+ "Send special information tone",
+ "Misdialled trunk prefix (national use)",
+ "Channel unacceptable",
+ "Call awarded and being delivered in an established channel",
+ "Preemption",
+ "Preemption - circuit reserved for reuse",
+
+/*10*/ "cause code 10: error, unassigned in Q.850 (03/93)",
+ "cause code 11: error, unassigned in Q.850 (03/93)",
+ "cause code 12: error, unassigned in Q.850 (03/93)",
+ "cause code 13: error, unassigned in Q.850 (03/93)",
+ "cause code 14: error, unassigned in Q.850 (03/93)",
+ "cause code 15: error, unassigned in Q.850 (03/93)",
+ "Normal call clearing",
+ "User busy",
+ "No user responding",
+ "No answer from user (user alerted)",
+
+/*20*/ "Subscriber absent",
+ "Call rejected",
+ "Number changed",
+ "cause code 23: error, unassigned in Q.850 (03/93)",
+ "cause code 24: error, unassigned in Q.850 (03/93)",
+ "cause code 25: error, unassigned in Q.850 (03/93)",
+ "Non-selected user clearing",
+ "Destination out of order",
+ "Invalid number format",
+ "Facility rejected",
+
+/*30*/ "Response to STATUS ENQUIRY",
+ "Normal, unspecified",
+ "cause code 32: error, unassigned in Q.850 (03/93)",
+ "cause code 33: error, unassigned in Q.850 (03/93)",
+ "No circuit / channel available",
+ "cause code 35: error, unassigned in Q.850 (03/93)",
+ "cause code 36: error, unassigned in Q.850 (03/93)",
+ "cause code 37: error, unassigned in Q.850 (03/93)",
+ "Network out of order",
+ "Permanent frame mode connection out of service",
+
+/*40*/ "Permanent frame mode connection operational",
+ "Temporary failure",
+ "Switching equipment congestion",
+ "Access information discarded",
+ "Requested circuit/channel not available",
+ "cause code 45: error, unassigned in Q.850 (03/93)",
+ "Precedence call blocked",
+ "Resources unavailable, unspecified",
+ "cause code 48: error, unassigned in Q.850 (03/93)",
+ "Quality of service unavailable",
+
+/*50*/ "Requested facility not subscribed",
+ "cause code 51: error, unassigned in Q.850 (03/93)",
+ "cause code 52: error, unassigned in Q.850 (03/93)",
+ "Outgoing calls barred within CUG",
+ "cause code 54: error, unassigned in Q.850 (03/93)",
+ "Incoming calls barred within CUG",
+ "cause code 56: error, unassigned in Q.850 (03/93)",
+ "Bearer capability not authorized",
+ "Bearer capability not presently available",
+ "cause code 59: error, unassigned in Q.850 (03/93)",
+
+/*60*/ "cause code 60: error, unassigned in Q.850 (03/93)",
+ "cause code 61: error, unassigned in Q.850 (03/93)",
+ "Inconsistenciy in designated outg. access info and subscriber class",
+ "Service or option not available, unspecified",
+ "cause code 64: error, unassigned in Q.850 (03/93)",
+ "Bearer capability not implemented",
+ "Channel type not implemented",
+ "cause code 67: error, unassigned in Q.850 (03/93)",
+ "cause code 68: error, unassigned in Q.850 (03/93)",
+ "Requested facility not implemented",
+
+/*70*/ "Only restricted digital information bearer capability is available",
+ "cause code 71: error, unassigned in Q.850 (03/93)",
+ "cause code 72: error, unassigned in Q.850 (03/93)",
+ "cause code 73: error, unassigned in Q.850 (03/93)",
+ "cause code 74: error, unassigned in Q.850 (03/93)",
+ "cause code 75: error, unassigned in Q.850 (03/93)",
+ "cause code 76: error, unassigned in Q.850 (03/93)",
+ "cause code 77: error, unassigned in Q.850 (03/93)",
+ "cause code 78: error, unassigned in Q.850 (03/93)",
+ "Service or option not implemented, unspecified",
+
+/*80*/ "cause code 80: error, unassigned in Q.850 (03/93)",
+ "Invalid call reference value",
+ "Identified channel does not exist",
+ "A suspended call exists, but this call identity does not",
+ "Call identity in use",
+ "No call suspended",
+ "Call having the requested call identity has been cleared",
+ "User not member of CUG",
+ "Incompatible destination",
+ "cause code 89: error, unassigned in Q.850 (03/93)",
+
+/*90*/ "Non-existent CUG",
+ "Invalid transit network selection",
+ "cause code 92: error, unassigned in Q.850 (03/93)",
+ "cause code 93: error, unassigned in Q.850 (03/93)",
+ "cause code 94: error, unassigned in Q.850 (03/93)",
+ "Invalid message, unspecified",
+ "Mandatory information element is missing",
+ "Message type non-existent or not implemented",
+ "Message not compatible with call state or message type non-existent or not implemented",
+ "Information element/parameter non-existent or not implemented",
+
+/*100*/ "Invalid information element contents",
+ "Message not compatible with call state",
+ "Recovery on timer expiry",
+ "Parameter non-existent or not implemented, passed on",
+ "cause code 104: error, unassigned in Q.850 (03/93)",
+ "cause code 105: error, unassigned in Q.850 (03/93)",
+ "cause code 106: error, unassigned in Q.850 (03/93)",
+ "cause code 107: error, unassigned in Q.850 (03/93)",
+ "cause code 108: error, unassigned in Q.850 (03/93)",
+ "cause code 109: error, unassigned in Q.850 (03/93)",
+
+/*110*/ "Message with unrecognized parameter, discarded",
+ "Protocol error, unspecified",
+ "cause code 112: error, unassigned in Q.850 (03/93)",
+ "cause code 113: error, unassigned in Q.850 (03/93)",
+ "cause code 114: error, unassigned in Q.850 (03/93)",
+ "cause code 115: error, unassigned in Q.850 (03/93)",
+ "cause code 116: error, unassigned in Q.850 (03/93)",
+ "cause code 117: error, unassigned in Q.850 (03/93)",
+ "cause code 118: error, unassigned in Q.850 (03/93)",
+ "cause code 119: error, unassigned in Q.850 (03/93)",
+
+/*120*/ "cause code 120: error, unassigned in Q.850 (03/93)",
+ "cause code 121: error, unassigned in Q.850 (03/93)",
+ "cause code 122: error, unassigned in Q.850 (03/93)",
+ "cause code 123: error, unassigned in Q.850 (03/93)",
+ "cause code 124: error, unassigned in Q.850 (03/93)",
+ "cause code 125: error, unassigned in Q.850 (03/93)",
+ "cause code 126: error, unassigned in Q.850 (03/93)",
+ "Interworking, unspecified"
+};
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/process.c b/usr.sbin/i4b/isdnd/process.c
new file mode 100644
index 0000000..f546043
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/process.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - process handling routines
+ * --------------------------------------
+ *
+ * $Id: process.c,v 1.8 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:48:19 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+/*---------------------------------------------------------------------------*
+ * check if another instance of us is already running
+ *---------------------------------------------------------------------------*/
+void
+check_pid(void)
+{
+ FILE *fp;
+
+ /* check if another lock-file already exists */
+
+ if((fp = fopen(PIDFILE, "r")) != NULL)
+ {
+ /* lockfile found, check */
+
+ int oldpid;
+
+ /* read pid from file */
+
+ if((fscanf(fp, "%d", &oldpid)) != 1)
+ {
+ log(LL_ERR, "ERROR, reading pid from lockfile failed, terminating!");
+ exit(1);
+ }
+
+ /* check if process got from file is still alive */
+
+ if((kill(oldpid, 0)) != 0)
+ {
+ /* process does not exist */
+
+ /* close file */
+
+ fclose(fp);
+
+ DBGL(DL_PROC, (log(LL_DBG, "removing old lock-file %s", PIDFILE)));
+
+ /* remove file */
+
+ unlink(PIDFILE);
+ }
+ else
+ {
+ /* process is still alive */
+
+ log(LL_ERR, "ERROR, another daemon is already running, pid = %d, terminating!", oldpid);
+ exit(1);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * establish and init process lock file
+ *---------------------------------------------------------------------------*/
+void
+write_pid(void)
+{
+ FILE *fp;
+
+ /* write my pid into lock-file */
+
+ if((fp = fopen(PIDFILE, "w")) == NULL)
+ {
+ log(LL_ERR, "ERROR, can't open lockfile for writing, terminating");
+ do_exit(1);
+ }
+
+ if((fprintf(fp, "%d", (int)getpid())) == EOF)
+ {
+ log(LL_ERR, "ERROR, can't write pid to lockfile, terminating");
+ do_exit(1);
+ }
+
+ fsync(fileno(fp));
+
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * become a daemon
+ *---------------------------------------------------------------------------*/
+void
+daemonize(void)
+{
+ int fd;
+
+ switch (fork())
+ {
+ case -1: /* error */
+ log(LL_ERR, "ERROR, daemonize/fork: %s", strerror(errno));
+ exit(1);
+ case 0: /* child */
+ break;
+ default: /* parent */
+ exit(0);
+ }
+
+ /* new session / no control tty */
+
+ if(setsid() == -1)
+ {
+ log(LL_ERR, "ERROR, setsid returns: %s", strerror(errno));
+ exit(1);
+ }
+
+ /* go away from mounted dir */
+
+ chdir("/");
+
+ /* move i/o to another device ? */
+
+ if(do_fullscreen && do_rdev)
+ {
+ char *tp;
+
+ if((fd = open(rdev, O_RDWR, 0)) != -1)
+ {
+ if(!isatty(fd))
+ {
+ log(LL_ERR, "ERROR, device %s is not a tty!", rdev);
+ exit(1);
+ }
+ if((dup2(fd, STDIN_FILENO)) == -1)
+ {
+ log(LL_ERR, "ERROR, dup2 stdin: %s", strerror(errno));
+ exit(1);
+ }
+ if((dup2(fd, STDOUT_FILENO)) == -1)
+ {
+ log(LL_ERR, "ERROR, dup2 stdout: %s", strerror(errno));
+ exit(1);
+ }
+ if((dup2(fd, STDERR_FILENO)) == -1)
+ {
+ log(LL_ERR, "ERROR, dup2 stderr: %s", strerror(errno));
+ exit(1);
+ }
+ }
+ else
+ {
+ log(LL_ERR, "ERROR, cannot open redirected device: %s", strerror(errno));
+ exit(1);
+ }
+
+ if(fd > 2)
+ {
+ if((close(fd)) == -1)
+ {
+ log(LL_ERR, "ERROR, close in daemonize: %s", strerror(errno));
+ exit(1);
+ }
+ }
+
+ /* curses output && fork NEEDS controlling tty */
+
+ if((ioctl(STDIN_FILENO, TIOCSCTTY, (char *)NULL)) < 0)
+ {
+ log(LL_ERR, "ERROR, cannot setup tty as controlling terminal: %s", strerror(errno));
+ exit(1);
+ }
+
+ /* in case there is no environment ... */
+
+ if(((tp = getenv("TERM")) == NULL) || (*tp == '\0'))
+ {
+ if(do_ttytype == 0)
+ {
+ log(LL_ERR, "ERROR, no environment variable TERM found and -t not specified!");
+ exit(1);
+ }
+
+ if((setenv("TERM", ttype, 1)) != 0)
+ {
+ log(LL_ERR, "ERROR, setenv TERM=%s failed: %s", ttype, strerror(errno));
+ exit(1);
+ }
+ }
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/rates.c b/usr.sbin/i4b/isdnd/rates.c
new file mode 100644
index 0000000..29aaef4
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rates.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 1997 Gary Jennejohn. All rights reserved.
+ *
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - charging rates description file handling
+ * -----------------------------------------------------
+ *
+ * $Id: rates.c,v 1.11 2000/10/09 12:53:29 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:48:31 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+static char error[256];
+
+static int getrate(int rate_type);
+
+#ifdef PARSE_DEBUG_MAIN
+
+#include <stdio.h>
+
+#define MAIN
+
+#define ERROR (-1)
+
+extern int got_rate;
+
+int main( int argc, char **argv )
+{
+ int ret;
+ ret = readrates("/etc/isdn/isdnd.rates");
+ if(ret == ERROR)
+ fprintf(stderr, "readrates returns [%d], [%s]\n", ret, error);
+ else
+ {
+ int type = 0;
+
+ got_rate = 1;
+
+ fprintf(stderr, "readrates returns [%d]\n", ret);
+
+ for( type=0; type<4; type++ )
+ {
+ int unit = getrate( type );
+ fprintf(stderr, "getrate(%d) => %d\n", type, unit );
+ }
+ }
+
+ return(ret);
+}
+
+#endif
+
+#include "isdnd.h"
+
+/*---------------------------------------------------------------------------*
+ * parse rates file
+ *---------------------------------------------------------------------------*/
+int
+readrates(char *filename)
+{
+ char buffer[MAXPATHLEN];
+ register char *bp;
+ struct rates *rt, *ort;
+ int rateindx;
+ int indx;
+ int line = 0;
+ FILE *fp;
+ int first;
+#if DEBUG
+ int i, j;
+#endif
+
+ indx = 0;
+ rt = ort = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ snprintf(error, sizeof(error), "error open %s: %s", filename, sys_errlist[errno]);
+ rate_error = error;
+ return(WARNING);
+ }
+
+ while((fgets(buffer, MAXPATHLEN, fp)) != NULL)
+ {
+ line++;
+
+/* comments */
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ bp = &buffer[0];
+
+ /* rate type */
+
+ if (*bp == 'r' && *(bp+1) == 'a' && isdigit(*(bp+2)))
+ {
+ rateindx = *(bp+2) - '0';
+ bp += 3;
+
+ /* eat space delimiter */
+
+ while(isspace(*bp))
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: invalid rate type %c%c%c in line %d", *bp, *(bp+1), *(bp+2), line);
+ goto rate_error;
+ }
+ if (rateindx >= NRATES)
+ {
+ snprintf(error, sizeof(error), "rates: invalid rate index %d in line %d", rateindx, line);
+ goto rate_error;
+ }
+
+ /* day */
+
+ if(isdigit(*bp) && *bp >= '0' && *bp <= '6')
+ {
+ indx = *bp - '0';
+
+ DBGL(DL_RATES, (log(LL_DBG, "rates: index = %d", indx)));
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: invalid day digit %c in line %d", *bp, line);
+ goto rate_error;
+ }
+
+ if(rates[rateindx][indx] == NULL)
+ {
+ rt = (struct rates *)malloc(sizeof (struct rates));
+ if (rt == NULL)
+ {
+ snprintf(error, sizeof(error), "rates: cannot malloc space for rate structure");
+ goto rate_error;
+ }
+ rt->next = NULL;
+ rates[rateindx][indx] = rt;
+ }
+
+ bp++;
+
+ /* eat space delimiter */
+
+ while(isspace(*bp))
+ bp++;
+
+ /* now loop to get the rates entries */
+
+ first = 1;
+
+ while(*bp && isdigit(*bp))
+ {
+ int hour = 0;
+ int min = 0;
+
+ if(first)
+ {
+ first = 0;
+ }
+ else
+ {
+ ort = rt;
+
+ rt = (struct rates *)malloc(sizeof (struct rates));
+ if (rt == NULL)
+ {
+ snprintf(error, sizeof(error), "rates: cannot malloc space2 for rate structure");
+ goto rate_error;
+ }
+ ort->next = rt;
+ rt->next = NULL;
+ }
+
+ /* start hour */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ hour = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: start_hr error in line %d", line);
+ goto rate_error;
+ }
+
+ /* point */
+
+ if(*bp == '.')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no '.' after start_hr in line %d", line);
+ goto rate_error;
+ }
+
+ /* start minute */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ min = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: start_min error in line %d", line);
+ goto rate_error;
+ }
+
+ rt->start_time = hour*60 + min;
+
+ /* minus */
+
+ if(*bp == '-')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no '-' after start_min in line %d", line);
+ goto rate_error;
+ }
+
+ /* end hour */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ hour = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: end_hr error in line %d", line);
+ goto rate_error;
+ }
+
+ /* point */
+
+ if(*bp == '.')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no '.' after end_hr in line %d", line);
+ goto rate_error;
+ }
+
+ /* end minute */
+
+ if(isdigit(*bp) && isdigit(*(bp+1)))
+ {
+ min = atoi(bp);
+ bp += 2;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: end_min error in line %d", line);
+ goto rate_error;
+ }
+
+ /* if hour is 0 assume it means midnight */
+ if( hour == 0 )
+ hour = 24;
+ rt->end_time = hour * 60 + min;
+
+ if( rt->end_time <= rt->start_time )
+ {
+ snprintf(error, sizeof(error), "rates: end_time must be greater then start_time %d", line);
+ goto rate_error;
+ }
+
+ /* colon */
+
+ if(*bp == ':')
+ {
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: no ':' after end_min in line %d", line);
+ goto rate_error;
+ }
+
+ /* time */
+
+ if(isdigit(*bp))
+ {
+ rt->rate = atoi(bp);
+ while(!isspace(*bp))
+ bp++;
+ }
+ else
+ {
+ snprintf(error, sizeof(error), "rates: first rate digit error in line %d", line);
+ goto rate_error;
+ }
+
+ /* eat space delimiter */
+
+ while(isspace(*bp))
+ bp++;
+ }
+ }
+
+#if DEBUG
+ if(debug_flags & DL_RATES)
+ {
+ for (j = 0; j < NRATES; j++)
+ {
+ for (i = 0; i < NDAYS; i++)
+ {
+ if (rates [j][i] != NULL)
+ {
+ rt = rates [j][i];
+ for (; rt; rt = rt->next)
+ {
+ log(LL_DBG, "rates: index %d day %d = %d.%2.2d-%d.%2.2d:%d",
+ j, i, rt->start_time/60, rt->start_time%60,
+ rt->end_time/60,rt->end_time%60,rt->rate);
+ }
+ }
+ else
+ {
+ log(LL_DBG, "rates: NO entry for day %d !!\n", i);
+ }
+ }
+ }
+ }
+#endif
+ fclose(fp);
+ return(GOOD);
+
+rate_error:
+ fclose(fp);
+ rate_error = error;
+ return(ERROR);
+}
+
+#ifndef PARSE_DEBUG_MAIN
+
+/*---------------------------------------------------------------------------*
+ * get unit length time from configured source
+ *---------------------------------------------------------------------------*/
+int
+get_current_rate(cfg_entry_t *cep, int logit)
+{
+ int rt;
+
+ switch(cep->unitlengthsrc)
+ {
+ case ULSRC_CMDL: /* specified on commandline */
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (cmdl)",
+ cep->cdid, cep->name, unit_length);
+ return(unit_length);
+ break;
+
+ case ULSRC_CONF: /* get it from config file */
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (conf)",
+ cep->cdid, cep->name, cep->unitlength);
+ return(cep->unitlength);
+
+ case ULSRC_RATE: /* get it dynamic from ratesfile*/
+ if(!got_rate) /* got valid rates struct ?? */
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (no ratefile)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+ return(UNITLENGTH_DEFAULT);
+ }
+ if((cep->ratetype >= NRATES) ||
+ (cep->ratetype == INVALID_RATE))
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (rate out of range)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+ return(UNITLENGTH_DEFAULT);
+ }
+
+ if((rt = getrate(cep->ratetype)) != -1)
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (rate)",
+ cep->cdid, cep->name, rt);
+ return(rt);
+ }
+
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (ratescan fail)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+
+ return(UNITLENGTH_DEFAULT);
+ break;
+
+ case ULSRC_DYN: /* dynamically calculated from AOC */
+ if((rt = getrate(cep->ratetype)) != -1)
+ {
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (aocd, rate)",
+ cep->cdid, cep->name, rt);
+ return(rt);
+ }
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (aocd, default)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+
+ return(UNITLENGTH_DEFAULT);
+ break;
+
+ default:
+ if(logit)
+ log(LL_CHD, "%05d %s rate %d sec/unit (unitlen unknown)",
+ cep->cdid, cep->name, UNITLENGTH_DEFAULT);
+
+ return(UNITLENGTH_DEFAULT);
+ break;
+ }
+}
+#endif /* PARSE_DEBUG_MAIN */
+
+
+/*---------------------------------------------------------------------------*
+ * get the currently active rate
+ *---------------------------------------------------------------------------*/
+static int
+getrate(int rate_type )
+{
+ struct tm *ptr;
+ time_t now;
+ register struct rates *hd;
+ int time_now;
+
+ if((!got_rate) ||
+ (rate_type >= NRATES) ||
+ (rate_type == INVALID_RATE))
+ {
+ return -1;
+ }
+
+ time(&now); /* get current time */
+
+ ptr = localtime(&now);
+
+ time_now = ptr->tm_hour*60 + ptr->tm_min;
+
+ /* walk thru the rates for weekday until rate for current time found */
+
+ for (hd = rates[rate_type][ptr->tm_wday]; hd; hd = hd->next)
+ {
+ /* current time within window ? */
+ if((time_now >= hd->start_time ) &&
+ (time_now < hd->end_time ))
+ {
+ DBGL(DL_RATES, (log(LL_DBG, "rate=%d sec/unit (day=%d, beg=%d:%2.2d, end=%d:2.2d, current=%d:%2.2d)",
+ hd->rate,
+ ptr->tm_wday,
+ hd->start_time/60, hd->start_time%60,
+ hd->end_time/60, hd->end_time%60,
+ time_now/60, time_now%60)));
+
+ return hd->rate;
+ }
+ }
+ return -1;
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/rc_config.c b/usr.sbin/i4b/isdnd/rc_config.c
new file mode 100644
index 0000000..6504747
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_config.c
@@ -0,0 +1,1865 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - config file processing
+ * -----------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Aug 11 12:30:49 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+
+#include "isdnd.h"
+#include "y.tab.h"
+
+#include "monitor.h"
+
+extern int entrycount;
+extern int controllercount;
+extern int lineno;
+extern char *yytext;
+
+extern FILE *yyin;
+extern int yyparse();
+
+static void set_config_defaults(void);
+static void check_config(void);
+static void print_config(void);
+static void parse_valid(int entrycount, char *dt);
+
+static int nregexpr = 0;
+static int nregprog = 0;
+
+/*---------------------------------------------------------------------------*
+ * called from main to read and process config file
+ *---------------------------------------------------------------------------*/
+void
+configure(char *filename, int reread)
+{
+ extern void reset_scanner(FILE *inputfile);
+
+ set_config_defaults();
+
+ yyin = fopen(filename, "r");
+
+ if(yyin == NULL)
+ {
+ log(LL_ERR, "cannot fopen file [%s]", filename);
+ exit(1);
+ }
+
+ if(reread)
+ {
+ reset_scanner(yyin);
+ }
+
+ yyparse();
+
+ monitor_fixup_rights();
+
+ check_config(); /* validation and consistency check */
+
+ fclose(yyin);
+
+ if(do_print)
+ {
+ if(config_error_flag)
+ {
+ log(LL_ERR, "there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ exit(1);
+ }
+ print_config();
+ do_exit(0);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * yacc error routine
+ *---------------------------------------------------------------------------*/
+void
+yyerror(const char *msg)
+{
+ log(LL_ERR, "configuration error: %s at line %d, token \"%s\"", msg, lineno+1, yytext);
+ config_error_flag++;
+}
+
+/*---------------------------------------------------------------------------*
+ * fill all config entries with default values
+ *---------------------------------------------------------------------------*/
+static void
+set_config_defaults(void)
+{
+ cfg_entry_t *cep = &cfg_entry_tab[0]; /* ptr to config entry */
+ int i;
+
+ /* system section cleanup */
+
+ nregprog = nregexpr = 0;
+
+ rt_prio = RTPRIO_NOTUSED;
+
+ mailer[0] = '\0';
+ mailto[0] = '\0';
+
+ /* clean regular expression table */
+
+ for(i=0; i < MAX_RE; i++)
+ {
+ if(rarr[i].re_expr)
+ free(rarr[i].re_expr);
+ rarr[i].re_expr = NULL;
+
+ if(rarr[i].re_prog)
+ free(rarr[i].re_prog);
+ rarr[i].re_prog = NULL;
+
+ rarr[i].re_flg = 0;
+ }
+
+ strcpy(rotatesuffix, "");
+
+ /*
+ * controller table cleanup, beware: has already
+ * been setup in main, init_controller() !
+ */
+
+ for(i=0; i < ncontroller; i++)
+ {
+ isdn_ctrl_tab[i].protocol = PROTOCOL_DSS1;
+ isdn_ctrl_tab[i].firmware = NULL;
+ }
+
+ /* entry section cleanup */
+
+ for(i=0; i < CFG_ENTRY_MAX; i++, cep++)
+ {
+ bzero(cep, sizeof(cfg_entry_t));
+
+ /* ====== filled in at startup configuration, then static */
+
+ sprintf(cep->name, "ENTRY%d", i);
+
+ cep->isdncontroller = INVALID;
+ cep->isdnchannel = CHAN_ANY;
+
+ cep->usrdevicename = INVALID;
+ cep->usrdeviceunit = INVALID;
+
+ cep->remote_numbers_handling = RNH_LAST;
+
+ cep->dialin_reaction = REACT_IGNORE;
+
+ cep->b1protocol = BPROT_NONE;
+
+ cep->unitlength = UNITLENGTH_DEFAULT;
+
+ cep->earlyhangup = EARLYHANGUP_DEFAULT;
+
+ cep->ratetype = INVALID_RATE;
+
+ cep->unitlengthsrc = ULSRC_NONE;
+
+ cep->answerprog = ANSWERPROG_DEF;
+
+ cep->callbackwait = CALLBACKWAIT_MIN;
+
+ cep->calledbackwait = CALLEDBACKWAIT_MIN;
+
+ cep->dialretries = DIALRETRIES_DEF;
+
+ cep->recoverytime = RECOVERYTIME_MIN;
+
+ cep->dialouttype = DIALOUT_NORMAL;
+
+ cep->inout = DIR_INOUT;
+
+ cep->ppp_expect_auth = AUTH_UNDEF;
+
+ cep->ppp_send_auth = AUTH_UNDEF;
+
+ cep->ppp_auth_flags = AUTH_RECHALLENGE | AUTH_REQUIRED;
+
+ /* ======== filled in after start, then dynamic */
+
+ cep->cdid = CDID_UNUSED;
+
+ cep->state = ST_IDLE;
+
+ cep->aoc_valid = AOC_INVALID;
+
+ cep->usesubaddr = 0;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * internaly set values for ommitted controler sectin
+ *---------------------------------------------------------------------------*/
+void
+cfg_set_controller_default()
+{
+ controllercount = 0;
+ DBGL(DL_RCCF, (log(LL_DBG, "[defaults, no controller section] controller %d: protocol = dss1", controllercount)));
+ isdn_ctrl_tab[controllercount].protocol = PROTOCOL_DSS1;
+}
+
+#define PPP_PAP 0xc023
+#define PPP_CHAP 0xc223
+
+static void
+set_isppp_auth(int entry)
+{
+ cfg_entry_t *cep = &cfg_entry_tab[entry]; /* ptr to config entry */
+
+ struct ifreq ifr;
+ struct spppreq spr;
+ int s;
+ int doioctl = 0;
+
+ if(cep->usrdevicename != BDRV_ISPPP)
+ return;
+
+ if(cep->ppp_expect_auth == AUTH_UNDEF
+ && cep->ppp_send_auth == AUTH_UNDEF)
+ return;
+
+ if(cep->ppp_expect_auth == AUTH_NONE
+ || cep->ppp_send_auth == AUTH_NONE)
+ doioctl = 1;
+
+ if ((cep->ppp_expect_auth == AUTH_CHAP
+ || cep->ppp_expect_auth == AUTH_PAP)
+ && cep->ppp_expect_name[0] != 0
+ && cep->ppp_expect_password[0] != 0)
+ doioctl = 1;
+
+ if ((cep->ppp_send_auth == AUTH_CHAP || cep->ppp_send_auth == AUTH_PAP)
+ && cep->ppp_send_name[0] != 0
+ && cep->ppp_send_password[0] != 0)
+ doioctl = 1;
+
+ if(!doioctl)
+ return;
+
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "isp%d", cep->usrdeviceunit);
+
+ /* use a random AF to create the socket */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ log(LL_ERR, "ERROR opening control socket at line %d!", lineno);
+ config_error_flag++;
+ return;
+ }
+ spr.cmd = (int)SPPPIOGDEFS;
+ ifr.ifr_data = (caddr_t)&spr;
+
+ if (ioctl(s, SIOCGIFGENERIC, &ifr) == -1) {
+ log(LL_ERR, "ERROR fetching active PPP authentication info for %s at line %d!", ifr.ifr_name, lineno);
+ close(s);
+ config_error_flag++;
+ return;
+ }
+ if (cep->ppp_expect_auth != AUTH_UNDEF)
+ {
+ if(cep->ppp_expect_auth == AUTH_NONE)
+ {
+ spr.defs.hisauth.proto = 0;
+ }
+ else if ((cep->ppp_expect_auth == AUTH_CHAP
+ || cep->ppp_expect_auth == AUTH_PAP)
+ && cep->ppp_expect_name[0] != 0
+ && cep->ppp_expect_password[0] != 0)
+ {
+ spr.defs.hisauth.proto = cep->ppp_expect_auth == AUTH_PAP ? PPP_PAP : PPP_CHAP;
+ strncpy(spr.defs.hisauth.name, cep->ppp_expect_name, AUTHNAMELEN);
+ strncpy(spr.defs.hisauth.secret, cep->ppp_expect_password, AUTHKEYLEN);
+ }
+ }
+ if (cep->ppp_send_auth != AUTH_UNDEF)
+ {
+ if(cep->ppp_send_auth == AUTH_NONE)
+ {
+ spr.defs.myauth.proto = 0;
+ }
+ else if ((cep->ppp_send_auth == AUTH_CHAP
+ || cep->ppp_send_auth == AUTH_PAP)
+ && cep->ppp_send_name[0] != 0
+ && cep->ppp_send_password[0] != 0)
+ {
+ spr.defs.myauth.proto = cep->ppp_send_auth == AUTH_PAP ? PPP_PAP : PPP_CHAP;
+ strncpy(spr.defs.myauth.name, cep->ppp_send_name, AUTHNAMELEN);
+ strncpy(spr.defs.myauth.secret, cep->ppp_send_password, AUTHKEYLEN);
+
+ if(cep->ppp_auth_flags & AUTH_REQUIRED)
+ spr.defs.hisauth.flags &= ~AUTHFLAG_NOCALLOUT;
+ else
+ spr.defs.hisauth.flags |= AUTHFLAG_NOCALLOUT;
+
+ if(cep->ppp_auth_flags & AUTH_RECHALLENGE)
+ spr.defs.hisauth.flags &= ~AUTHFLAG_NORECHALLENGE;
+ else
+ spr.defs.hisauth.flags |= AUTHFLAG_NORECHALLENGE;
+ }
+ }
+
+ spr.cmd = (int)SPPPIOSDEFS;
+
+ if (ioctl(s, SIOCSIFGENERIC, &ifr) == -1) {
+ log(LL_ERR, "ERROR setting new PPP authentication parameters for %s at line %d!", ifr.ifr_name, lineno);
+ config_error_flag++;
+ }
+ close(s);
+}
+
+/*---------------------------------------------------------------------------*
+ * extract values from config and fill table
+ *---------------------------------------------------------------------------*/
+void
+cfg_setval(int keyword)
+{
+ int i;
+
+ switch(keyword)
+ {
+ case ACCTALL:
+ acct_all = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: acctall = %d", yylval.booln)));
+ break;
+
+ case ACCTFILE:
+ strcpy(acctfile, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: acctfile = %s", yylval.str)));
+ break;
+
+ case ADDPREFIX:
+ addprefix = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: add-prefix = %d", yylval.booln)));
+ break;
+
+ case ALERT:
+ if(yylval.num < MINALERT)
+ {
+ yylval.num = MINALERT;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: alert < %d, min = %d", entrycount, MINALERT, yylval.num)));
+ }
+ else if(yylval.num > MAXALERT)
+ {
+ yylval.num = MAXALERT;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: alert > %d, min = %d", entrycount, MAXALERT, yylval.num)));
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: alert = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].alert = yylval.num;
+ break;
+
+ case ALIASING:
+ DBGL(DL_RCCF, (log(LL_DBG, "system: aliasing = %d", yylval.booln)));
+ aliasing = yylval.booln;
+ break;
+
+ case ALIASFNAME:
+ strcpy(aliasfile, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: aliasfile = %s", yylval.str)));
+ break;
+
+ case ANSWERPROG:
+ if((cfg_entry_tab[entrycount].answerprog = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: answerstring, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].answerprog, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: answerprog = %s", entrycount, yylval.str)));
+ break;
+
+ case B1PROTOCOL:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: b1protocol = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "raw")))
+ cfg_entry_tab[entrycount].b1protocol = BPROT_NONE;
+ else if(!(strcmp(yylval.str, "hdlc")))
+ cfg_entry_tab[entrycount].b1protocol = BPROT_RHDLC;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"b1protocol\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case BCAP:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: bcap = %s", entrycount, yylval.str)));
+ cfg_entry_tab[entrycount].bcap = BCAP_NONE;
+ if(!(strcmp(yylval.str, "dov")))
+ cfg_entry_tab[entrycount].bcap = BCAP_DOV;
+ break;
+
+ case BEEPCONNECT:
+ do_bell = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: beepconnect = %d", yylval.booln)));
+ break;
+
+ case BUDGETCALLBACKPERIOD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbackperiod = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_callbackperiod = yylval.num;
+ break;
+
+ case BUDGETCALLBACKNCALLS:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbackncalls = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_callbackncalls = yylval.num;
+ break;
+
+ case BUDGETCALLOUTPERIOD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutperiod = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_calloutperiod = yylval.num;
+ break;
+
+ case BUDGETCALLOUTNCALLS:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutncalls = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].budget_calloutncalls = yylval.num;
+ break;
+
+ case BUDGETCALLBACKSFILEROTATE:
+ cfg_entry_tab[entrycount].budget_callbacksfile_rotate = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbacksfile-rotate = %d", entrycount, yylval.booln)));
+ break;
+
+ case BUDGETCALLBACKSFILE:
+ {
+ FILE *fp;
+ int s, l;
+ int n;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-callbacksfile = %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) != 3)
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: initializing budget-callbacksfile %s", entrycount, yylval.str)));
+ fclose(fp);
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+ }
+ else
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: creating budget-callbacksfile %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) == 3)
+ {
+ if((cfg_entry_tab[entrycount].budget_callbacks_file = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: budget-callbacksfile, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].budget_callbacks_file, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: using callbacksfile %s", entrycount, yylval.str)));
+ }
+ fclose(fp);
+ }
+ }
+ break;
+
+ case BUDGETCALLOUTSFILEROTATE:
+ cfg_entry_tab[entrycount].budget_calloutsfile_rotate = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutsfile-rotate = %d", entrycount, yylval.booln)));
+ break;
+
+ case BUDGETCALLOUTSFILE:
+ {
+ FILE *fp;
+ int s, l;
+ int n;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: budget-calloutsfile = %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) != 3)
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: initializing budget-calloutsfile %s", entrycount, yylval.str)));
+ fclose(fp);
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+ }
+ else
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: creating budget-calloutsfile %s", entrycount, yylval.str)));
+ fp = fopen(yylval.str, "w");
+ if(fp != NULL)
+ {
+ fprintf(fp, "%d %d %d", (int)time(NULL), (int)time(NULL), 0);
+ fclose(fp);
+ }
+ }
+
+ fp = fopen(yylval.str, "r");
+ if(fp != NULL)
+ {
+ if((fscanf(fp, "%d %d %d", (int *)&s, (int *)&l, &n)) == 3)
+ {
+ if((cfg_entry_tab[entrycount].budget_callouts_file = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: budget-calloutsfile, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].budget_callouts_file, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: using calloutsfile %s", entrycount, yylval.str)));
+ }
+ fclose(fp);
+ }
+ }
+ break;
+
+ case CALLBACKWAIT:
+ if(yylval.num < CALLBACKWAIT_MIN)
+ {
+ yylval.num = CALLBACKWAIT_MIN;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: callbackwait < %d, min = %d", entrycount, CALLBACKWAIT_MIN, yylval.num)));
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: callbackwait = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].callbackwait = yylval.num;
+ break;
+
+ case CALLEDBACKWAIT:
+ if(yylval.num < CALLEDBACKWAIT_MIN)
+ {
+ yylval.num = CALLEDBACKWAIT_MIN;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: calledbackwait < %d, min = %d", entrycount, CALLEDBACKWAIT_MIN, yylval.num)));
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: calledbackwait = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].calledbackwait = yylval.num;
+ break;
+
+ case CLONE:
+ /*
+ * clone = <entryname>
+ * Loads the entry from the named, existing one.
+ * Fields such as name and usrdeviceunit should
+ * always be specified after clone as they must be
+ * unique.
+ *
+ * NOTE: all malloc()'d fields must be dup()'d here,
+ * we can't have multiple references to same storage.
+ */
+ for (i = 0; i < entrycount; i++)
+ if (!strcmp(cfg_entry_tab[i].name, yylval.str))
+ break;
+ if (i == entrycount) {
+ log(LL_ERR, "entry %d: clone, unknown entry %s!", entrycount, yylval.str);
+ do_exit(1);
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: clone = %s", entrycount, yylval.str)));
+
+ memcpy(&cfg_entry_tab[entrycount], &cfg_entry_tab[i],
+ sizeof(cfg_entry_tab[0]));
+
+ if (cfg_entry_tab[entrycount].answerprog)
+ cfg_entry_tab[entrycount].answerprog = strdup(cfg_entry_tab[entrycount].answerprog);
+ if (cfg_entry_tab[entrycount].budget_callbacks_file)
+ cfg_entry_tab[entrycount].budget_callbacks_file = strdup(cfg_entry_tab[entrycount].budget_callbacks_file);
+ if (cfg_entry_tab[entrycount].budget_callouts_file)
+ cfg_entry_tab[entrycount].budget_callouts_file = strdup(cfg_entry_tab[entrycount].budget_callouts_file);
+ if (cfg_entry_tab[entrycount].connectprog)
+ cfg_entry_tab[entrycount].connectprog = strdup(cfg_entry_tab[entrycount].connectprog);
+ if (cfg_entry_tab[entrycount].disconnectprog)
+ cfg_entry_tab[entrycount].disconnectprog = strdup(cfg_entry_tab[entrycount].disconnectprog);
+ break;
+
+ case CONNECTPROG:
+ if((cfg_entry_tab[entrycount].connectprog = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: connectprog, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].connectprog, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: connectprog = %s", entrycount, yylval.str)));
+ break;
+
+ case DIALOUTTYPE:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialouttype = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "normal")))
+ cfg_entry_tab[entrycount].dialouttype = DIALOUT_NORMAL;
+ else if(!(strcmp(yylval.str, "calledback")))
+ cfg_entry_tab[entrycount].dialouttype = DIALOUT_CALLEDBACK;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"dialout-type\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case DIALRETRIES:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialretries = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].dialretries = yylval.num;
+ break;
+
+ case DIALRANDINCR:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialrandincr = %d", entrycount, yylval.booln)));
+ cfg_entry_tab[entrycount].dialrandincr = yylval.booln;
+ break;
+
+ case DIRECTION:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: direction = %s", entrycount, yylval.str)));
+
+ if(!(strcmp(yylval.str, "inout")))
+ cfg_entry_tab[entrycount].inout = DIR_INOUT;
+ else if(!(strcmp(yylval.str, "in")))
+ cfg_entry_tab[entrycount].inout = DIR_INONLY;
+ else if(!(strcmp(yylval.str, "out")))
+ cfg_entry_tab[entrycount].inout = DIR_OUTONLY;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"direction\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case DISCONNECTPROG:
+ if((cfg_entry_tab[entrycount].disconnectprog = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "entry %d: disconnectprog, malloc failed!", entrycount);
+ do_exit(1);
+ }
+ strcpy(cfg_entry_tab[entrycount].disconnectprog, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: disconnectprog = %s", entrycount, yylval.str)));
+ break;
+
+ case DOWNTRIES:
+ if(yylval.num > DOWN_TRIES_MAX)
+ yylval.num = DOWN_TRIES_MAX;
+ else if(yylval.num < DOWN_TRIES_MIN)
+ yylval.num = DOWN_TRIES_MIN;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: downtries = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].downtries = yylval.num;
+ break;
+
+ case DOWNTIME:
+ if(yylval.num > DOWN_TIME_MAX)
+ yylval.num = DOWN_TIME_MAX;
+ else if(yylval.num < DOWN_TIME_MIN)
+ yylval.num = DOWN_TIME_MIN;
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: downtime = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].downtime = yylval.num;
+ break;
+
+ case EARLYHANGUP:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: earlyhangup = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].earlyhangup = yylval.num;
+ break;
+
+ case EXTCALLATTR:
+ DBGL(DL_RCCF, (log(LL_DBG, "system: extcallattr = %d", yylval.booln)));
+ extcallattr = yylval.booln;
+ break;
+
+ case FIRMWARE:
+ DBGL(DL_RCCF, (log(LL_DBG, "controller %d: firmware = %s", controllercount, yylval.str)));
+ isdn_ctrl_tab[controllercount].firmware = strdup(yylval.str);
+ break;
+
+ case HOLIDAYFILE:
+ strcpy(holidayfile, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: holidayfile = %s", yylval.str)));
+ break;
+
+ case IDLE_ALG_OUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: idle-algorithm-outgoing = %s", entrycount, yylval.str)));
+
+ if(!(strcmp(yylval.str, "fix-unit-size")))
+ {
+ cfg_entry_tab[entrycount].shorthold_algorithm = SHA_FIXU;
+ }
+ else if(!(strcmp(yylval.str, "var-unit-size")))
+ {
+ cfg_entry_tab[entrycount].shorthold_algorithm = SHA_VARU;
+ }
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"idle-algorithm-outgoing\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case IDLETIME_IN:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: idle_time_in = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].idle_time_in = yylval.num;
+ break;
+
+ case IDLETIME_OUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: idle_time_out = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].idle_time_out = yylval.num;
+ break;
+
+ case ISDNCONTROLLER:
+ cfg_entry_tab[entrycount].isdncontroller = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdncontroller = %d", entrycount, yylval.num)));
+ break;
+
+ case ISDNCHANNEL:
+ if (yylval.num == 0 || yylval.num == -1)
+ {
+ cfg_entry_tab[entrycount].isdnchannel = CHAN_ANY;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = any", entrycount)));
+ }
+ else if (yylval.num > MAX_BCHAN)
+ {
+ log(LL_DBG, "entry %d: isdnchannel value out of range", entrycount);
+ config_error_flag++;
+ }
+ else
+ {
+ cfg_entry_tab[entrycount].isdnchannel = yylval.num-1;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = B%d", entrycount, yylval.num)));
+ }
+ break;
+
+ case ISDNTIME:
+ DBGL(DL_RCCF, (log(LL_DBG, "system: isdntime = %d", yylval.booln)));
+ isdntime = yylval.booln;
+ break;
+
+ case ISDNTXDELIN:
+ cfg_entry_tab[entrycount].isdntxdelin = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdntxdel-incoming = %d", entrycount, yylval.num)));
+ break;
+
+ case ISDNTXDELOUT:
+ cfg_entry_tab[entrycount].isdntxdelout = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdntxdel-outgoing = %d", entrycount, yylval.num)));
+ break;
+
+ case LOCAL_PHONE_DIALOUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_phone_dialout = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_dialout.number, yylval.str);
+ break;
+
+ case LOCAL_SUBADDR_DIALOUT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_subaddr_dialout = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_dialout.subaddr, yylval.str);
+ break;
+
+ case LOCAL_PHONE_INCOMING:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_phone_incoming = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_incoming.number, yylval.str);
+ break;
+
+ case LOCAL_SUBADDR_INCOMING:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: local_subaddr_incoming = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].local_phone_incoming.subaddr, yylval.str);
+ break;
+
+ case MAILER:
+ strcpy(mailer, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: mailer = %s", yylval.str)));
+ break;
+
+ case MAILTO:
+ strcpy(mailto, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: mailto = %s", yylval.str)));
+ break;
+
+ case MAXCONNECTTIME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: maxconnecttime = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].maxconnecttime = yylval.num;
+ break;
+
+ case MONITORPORT:
+ monitorport = yylval.num;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitorport = %d", yylval.num)));
+ break;
+
+ case MONITORSW:
+ if (yylval.booln && inhibit_monitor)
+ {
+ do_monitor = 0;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-enable overriden by command line flag")));
+ }
+ else
+ {
+ do_monitor = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-enable = %d", yylval.booln)));
+ }
+ break;
+
+ case NAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: name = %s", entrycount, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].name, yylval.str);
+ break;
+
+ case PPP_AUTH_RECHALLENGE:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-auth-rechallenge = %d", entrycount, yylval.booln)));
+ if(yylval.booln)
+ cfg_entry_tab[entrycount].ppp_auth_flags |= AUTH_RECHALLENGE;
+ else
+ cfg_entry_tab[entrycount].ppp_auth_flags &= ~AUTH_RECHALLENGE;
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_AUTH_PARANOID:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-auth-paranoid = %d", entrycount, yylval.booln)));
+ if(yylval.booln)
+ cfg_entry_tab[entrycount].ppp_auth_flags |= AUTH_REQUIRED;
+ else
+ cfg_entry_tab[entrycount].ppp_auth_flags &= ~AUTH_REQUIRED;
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_EXPECT_AUTH:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-expect-auth = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "none")))
+ cfg_entry_tab[entrycount].ppp_expect_auth = AUTH_NONE;
+ else if(!(strcmp(yylval.str, "pap")))
+ cfg_entry_tab[entrycount].ppp_expect_auth = AUTH_PAP;
+ else if(!(strcmp(yylval.str, "chap")))
+ cfg_entry_tab[entrycount].ppp_expect_auth = AUTH_CHAP;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"ppp-expect-auth\" at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_EXPECT_NAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-expect-name = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_expect_name, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_expect_name) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_EXPECT_PASSWORD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-expect-password = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_expect_password, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_expect_password) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_SEND_AUTH:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-send-auth = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "none")))
+ cfg_entry_tab[entrycount].ppp_send_auth = AUTH_NONE;
+ else if(!(strcmp(yylval.str, "pap")))
+ cfg_entry_tab[entrycount].ppp_send_auth = AUTH_PAP;
+ else if(!(strcmp(yylval.str, "chap")))
+ cfg_entry_tab[entrycount].ppp_send_auth = AUTH_CHAP;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"ppp-send-auth\" at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_SEND_NAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-send-name = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_send_name, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_send_name) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PPP_SEND_PASSWORD:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ppp-send-password = %s", entrycount, yylval.str)));
+ strncpy(cfg_entry_tab[entrycount].ppp_send_password, yylval.str, sizeof(cfg_entry_tab[entrycount].ppp_send_password) -1);
+ set_isppp_auth(entrycount);
+ break;
+
+ case PREFIXINTERNATIONAL:
+ strncpy(prefixinternational, yylval.str, sizeof(prefixinternational)-1);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: prefix-international = %s", prefixinternational)));
+ break;
+
+ case PREFIXNATIONAL:
+ strncpy(prefixnational, yylval.str, sizeof(prefixnational)-1);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: prefix-national = %s", prefixnational)));
+ break;
+
+ case PROTOCOL:
+ DBGL(DL_RCCF, (log(LL_DBG, "controller %d: protocol = %s", controllercount, yylval.str)));
+ if(!(strcmp(yylval.str, "dss1")))
+ isdn_ctrl_tab[controllercount].protocol = PROTOCOL_DSS1;
+ else if(!(strcmp(yylval.str, "d64s")))
+ isdn_ctrl_tab[controllercount].protocol = PROTOCOL_D64S;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"protocol\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case REACTION:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: dialin_reaction = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "accept")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_ACCEPT;
+ else if(!(strcmp(yylval.str, "reject")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_REJECT;
+ else if(!(strcmp(yylval.str, "ignore")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_IGNORE;
+ else if(!(strcmp(yylval.str, "answer")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_ANSWER;
+ else if(!(strcmp(yylval.str, "callback")))
+ cfg_entry_tab[entrycount].dialin_reaction = REACT_CALLBACK;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"dialin_reaction\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case REMOTE_PHONE_DIALOUT:
+ if(cfg_entry_tab[entrycount].remote_numbers_count >= MAXRNUMBERS)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many remote numbers at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_phone_dialout #%d = %s",
+ entrycount, cfg_entry_tab[entrycount].remote_numbers_count, yylval.str)));
+
+ strcpy(cfg_entry_tab[entrycount].remote_numbers[cfg_entry_tab[entrycount].remote_numbers_count].number, yylval.str);
+ cfg_entry_tab[entrycount].remote_numbers[cfg_entry_tab[entrycount].remote_numbers_count].flag = 0;
+
+ cfg_entry_tab[entrycount].remote_numbers_count++;
+
+ break;
+
+ case REMOTE_SUBADDR_DIALOUT:
+ if(cfg_entry_tab[entrycount].remote_subaddr_count >= MAXRNUMBERS)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many remote subaddresses at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_subaddr_dialout #%d = %s",
+ entrycount, cfg_entry_tab[entrycount].remote_numbers_count, yylval.str)));
+
+ strcpy(cfg_entry_tab[entrycount].remote_numbers[cfg_entry_tab[entrycount].remote_numbers_count].subaddr, yylval.str);
+
+ break;
+
+ case REMOTE_NUMBERS_HANDLING:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remdial_handling = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "next")))
+ cfg_entry_tab[entrycount].remote_numbers_handling = RNH_NEXT;
+ else if(!(strcmp(yylval.str, "last")))
+ cfg_entry_tab[entrycount].remote_numbers_handling = RNH_LAST;
+ else if(!(strcmp(yylval.str, "first")))
+ cfg_entry_tab[entrycount].remote_numbers_handling = RNH_FIRST;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"remdial_handling\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case REMOTE_PHONE_INCOMING:
+ {
+ int n;
+ n = cfg_entry_tab[entrycount].incoming_numbers_count;
+ if (n >= MAX_INCOMING)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many \"remote_phone_incoming\" entries at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_phone_incoming #%d = %s", entrycount, n, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].remote_phone_incoming[n].number, yylval.str);
+ cfg_entry_tab[entrycount].incoming_numbers_count++;
+ }
+ break;
+
+ case REMOTE_SUBADDR_INCOMING:
+ {
+ int n;
+ n = cfg_entry_tab[entrycount].incoming_numbers_count;
+ if (n >= MAX_INCOMING)
+ {
+ log(LL_ERR, "ERROR parsing config file: too many \"remote_subaddr_incoming\" entries at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: remote_subaddr_incoming #%d = %s", entrycount, n, yylval.str)));
+ strcpy(cfg_entry_tab[entrycount].remote_phone_incoming[n].subaddr, yylval.str);
+ }
+ break;
+
+ case RATESFILE:
+ strcpy(ratesfile, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: ratesfile = %s", yylval.str)));
+ break;
+
+ case RATETYPE:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: ratetype = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].ratetype = yylval.num;
+ break;
+
+ case RECOVERYTIME:
+ if(yylval.num < RECOVERYTIME_MIN)
+ {
+ yylval.num = RECOVERYTIME_MIN;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: recoverytime < %d, min = %d", entrycount, RECOVERYTIME_MIN, yylval.num)));
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: recoverytime = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].recoverytime = yylval.num;
+ break;
+
+ case REGEXPR:
+ if(nregexpr >= MAX_RE)
+ {
+ log(LL_ERR, "system: regexpr #%d >= MAX_RE", nregexpr);
+ config_error_flag++;
+ break;
+ }
+
+ if((i = regcomp(&(rarr[nregexpr].re), yylval.str, REG_EXTENDED|REG_NOSUB)) != 0)
+ {
+ char buf[256];
+ regerror(i, &(rarr[nregexpr].re), buf, sizeof(buf));
+ log(LL_ERR, "system: regcomp error for %s: [%s]", yylval.str, buf);
+ config_error_flag++;
+ break;
+ }
+ else
+ {
+ if((rarr[nregexpr].re_expr = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "system: regexpr malloc error error for %s", yylval.str);
+ config_error_flag++;
+ break;
+ }
+ strcpy(rarr[nregexpr].re_expr, yylval.str);
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: regexpr %s stored into slot %d", yylval.str, nregexpr)));
+
+ if(rarr[nregexpr].re_prog != NULL)
+ rarr[nregexpr].re_flg = 1;
+
+ nregexpr++;
+
+ }
+ break;
+
+ case REGPROG:
+ if(nregprog >= MAX_RE)
+ {
+ log(LL_ERR, "system: regprog #%d >= MAX_RE", nregprog);
+ config_error_flag++;
+ break;
+ }
+ if((rarr[nregprog].re_prog = malloc(strlen(yylval.str)+1)) == NULL)
+ {
+ log(LL_ERR, "system: regprog malloc error error for %s", yylval.str);
+ config_error_flag++;
+ break;
+ }
+ strcpy(rarr[nregprog].re_prog, yylval.str);
+
+ DBGL(DL_RCCF, (log(LL_DBG, "system: regprog %s stored into slot %d", yylval.str, nregprog)));
+
+ if(rarr[nregprog].re_expr != NULL)
+ rarr[nregprog].re_flg = 1;
+
+ nregprog++;
+ break;
+
+ case ROTATESUFFIX:
+ strcpy(rotatesuffix, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: rotatesuffix = %s", yylval.str)));
+ break;
+
+ case RTPRIO:
+#ifdef USE_RTPRIO
+ rt_prio = yylval.num;
+ if(rt_prio < RTP_PRIO_MIN || rt_prio > RTP_PRIO_MAX)
+ {
+ config_error_flag++;
+ log(LL_ERR, "system: error, rtprio (%d) out of range!", yylval.num);
+ }
+ else
+ {
+ DBGL(DL_RCCF, (log(LL_DBG, "system: rtprio = %d", yylval.num)));
+ }
+#else
+ rt_prio = RTPRIO_NOTUSED;
+#endif
+ break;
+
+ case TINAINITPROG:
+ strcpy(tinainitprog, yylval.str);
+ DBGL(DL_RCCF, (log(LL_DBG, "system: tinainitprog = %s", yylval.str)));
+ break;
+
+ case UNITLENGTH:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: unitlength = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].unitlength = yylval.num;
+ break;
+
+ case UNITLENGTHSRC:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: unitlengthsrc = %s", entrycount, yylval.str)));
+ if(!(strcmp(yylval.str, "none")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_NONE;
+ else if(!(strcmp(yylval.str, "cmdl")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_CMDL;
+ else if(!(strcmp(yylval.str, "conf")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_CONF;
+ else if(!(strcmp(yylval.str, "rate")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_RATE;
+ else if(!(strcmp(yylval.str, "aocd")))
+ cfg_entry_tab[entrycount].unitlengthsrc = ULSRC_DYN;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"unitlengthsrc\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case USRDEVICENAME:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usrdevicename = %s", entrycount, yylval.str)));
+ if(!strcmp(yylval.str, "rbch"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_RBCH;
+ else if(!strcmp(yylval.str, "tel"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_TEL;
+ else if(!strcmp(yylval.str, "ipr"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_IPR;
+ else if(!strcmp(yylval.str, "isp"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_ISPPP;
+#ifdef __bsdi__
+ else if(!strcmp(yylval.str, "ibc"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_IBC;
+#endif
+ else if(!strcmp(yylval.str, "ing"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_ING;
+ else
+ {
+ log(LL_ERR, "ERROR parsing config file: unknown parameter for keyword \"usrdevicename\" at line %d!", lineno);
+ config_error_flag++;
+ }
+ break;
+
+ case USRDEVICEUNIT:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usrdeviceunit = %d", entrycount, yylval.num)));
+ cfg_entry_tab[entrycount].usrdeviceunit = yylval.num;
+ break;
+
+ case USEACCTFILE:
+ useacctfile = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: useacctfile = %d", yylval.booln)));
+ break;
+
+ case USESUBADDR:
+ cfg_entry_tab[entrycount].usesubaddr = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usesubaddr = %d", entrycount, yylval.booln)));
+ break;
+
+ case USEDOWN:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usedown = %d", entrycount, yylval.booln)));
+ cfg_entry_tab[entrycount].usedown = yylval.booln;
+ break;
+
+ case VALID:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: valid = %s", entrycount, yylval.str)));
+ parse_valid(entrycount, yylval.str);
+ break;
+
+ default:
+ log(LL_ERR, "ERROR parsing config file: unknown keyword at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * parse a date/time range
+ *---------------------------------------------------------------------------*/
+static void
+parse_valid(int entrycount, char *dt)
+{
+ /* a valid string consists of some days of week separated by
+ * commas, where 0=sunday, 1=monday .. 6=saturday and a special
+ * value of 7 which is a holiday from the holiday file.
+ * after the days comes an optional (!) time range in the form
+ * aa:bb-cc:dd, this format is fixed to be parsable by sscanf.
+ * Valid specifications looks like this:
+ * 1,2,3,4,5,09:00-18:00 Monday-Friday 9-18h
+ * 1,2,3,4,5,18:00-09:00 Monday-Friday 18-9h
+ * 6 Saturday (whole day)
+ * 0,7 Sunday and Holidays
+ */
+
+ int day = 0;
+ int fromhr = 0;
+ int frommin = 0;
+ int tohr = 0;
+ int tomin = 0;
+ int ret;
+
+ for(;;)
+ {
+ if( ( ((*dt >= '0') && (*dt <= '9')) && (*(dt+1) == ':') ) ||
+ ( ((*dt >= '0') && (*dt <= '2')) && ((*(dt+1) >= '0') && (*(dt+1) <= '9')) && (*(dt+2) == ':') ) )
+ {
+ /* dt points to time spec */
+ ret = sscanf(dt, "%d:%d-%d:%d", &fromhr, &frommin, &tohr, &tomin);
+ if(ret !=4)
+ {
+ log(LL_ERR, "ERROR parsing config file: timespec [%s] error at line %d!", *dt, lineno);
+ config_error_flag++;
+ return;
+ }
+
+ if(fromhr < 0 || fromhr > 24 || tohr < 0 || tohr > 24 ||
+ frommin < 0 || frommin > 59 || tomin < 0 || tomin > 59)
+ {
+ log(LL_ERR, "ERROR parsing config file: invalid time [%s] at line %d!", *dt, lineno);
+ config_error_flag++;
+ return;
+ }
+ break;
+ }
+ else if ((*dt >= '0') && (*dt <= '7'))
+ {
+ /* dt points to day spec */
+ day |= 1 << (*dt - '0');
+ dt++;
+ continue;
+ }
+ else if (*dt == ',')
+ {
+ /* dt points to delimiter */
+ dt++;
+ continue;
+ }
+ else if (*dt == '\0')
+ {
+ /* dt points to end of string */
+ break;
+ }
+ else
+ {
+ /* dt points to illegal character */
+ log(LL_ERR, "ERROR parsing config file: illegal character [%c=0x%x] in date/time spec at line %d!", *dt, *dt, lineno);
+ config_error_flag++;
+ return;
+ }
+ }
+ cfg_entry_tab[entrycount].day = day;
+ cfg_entry_tab[entrycount].fromhr = fromhr;
+ cfg_entry_tab[entrycount].frommin = frommin;
+ cfg_entry_tab[entrycount].tohr = tohr;
+ cfg_entry_tab[entrycount].tomin = tomin;
+}
+
+/*---------------------------------------------------------------------------*
+ * configuration validation and consistency check
+ *---------------------------------------------------------------------------*/
+static void
+check_config(void)
+{
+ cfg_entry_t *cep = &cfg_entry_tab[0]; /* ptr to config entry */
+ int i;
+ int error = 0;
+
+ /* regular expression table */
+
+ for(i=0; i < MAX_RE; i++)
+ {
+ if((rarr[i].re_expr != NULL) && (rarr[i].re_prog == NULL))
+ {
+ log(LL_ERR, "check_config: regular expression %d without program!", i);
+ error++;
+ }
+ if((rarr[i].re_prog != NULL) && (rarr[i].re_expr == NULL))
+ {
+ log(LL_ERR, "check_config: regular expression program %d without expression!", i);
+ error++;
+ }
+ }
+
+ /* entry sections */
+
+ for(i=0; i <= entrycount; i++, cep++)
+ {
+ /* isdn controller number */
+
+ if((cep->isdncontroller < -1) || (cep->isdncontroller > (ncontroller-1)))
+ {
+ log(LL_ERR, "check_config: WARNING, isdncontroller out of range in entry %d!", i);
+ }
+
+ /* numbers used for dialout */
+
+ if((cep->inout != DIR_INONLY) && (cep->dialin_reaction != REACT_ANSWER))
+ {
+ if(cep->remote_numbers_count == 0)
+ {
+ log(LL_ERR, "check_config: remote-phone-dialout not set in entry %d!", i);
+ error++;
+ }
+ if(strlen(cep->local_phone_dialout.number) == 0)
+ {
+ log(LL_ERR, "check_config: local-phone-dialout not set in entry %d!", i);
+ error++;
+ }
+ }
+
+ /* numbers used for incoming calls */
+
+ if(cep->inout != DIR_OUTONLY)
+ {
+ if(strlen(cep->local_phone_incoming.number) == 0)
+ {
+ log(LL_ERR, "check_config: local-phone-incoming not set in entry %d!", i);
+ error++;
+ }
+ if(cep->incoming_numbers_count == 0)
+ {
+ log(LL_ERR, "check_config: remote-phone-incoming not set in entry %d!", i);
+ error++;
+ }
+ }
+
+ if((cep->dialin_reaction == REACT_ANSWER) && (cep->b1protocol != BPROT_NONE))
+ {
+ log(LL_ERR, "check_config: b1protocol not raw for telephony in entry %d!", i);
+ error++;
+ }
+
+ if((cep->ppp_send_auth == AUTH_PAP) || (cep->ppp_send_auth == AUTH_CHAP))
+ {
+ if(cep->ppp_send_name[0] == 0)
+ {
+ log(LL_ERR, "check_config: no remote authentification name in entry %d!", i);
+ error++;
+ }
+ if(cep->ppp_send_password[0] == 0)
+ {
+ log(LL_ERR, "check_config: no remote authentification password in entry %d!", i);
+ error++;
+ }
+ }
+ if((cep->ppp_expect_auth == AUTH_PAP) || (cep->ppp_expect_auth == AUTH_CHAP))
+ {
+ if(cep->ppp_expect_name[0] == 0)
+ {
+ log(LL_ERR, "check_config: no local authentification name in entry %d!", i);
+ error++;
+ }
+ if(cep->ppp_expect_password[0] == 0)
+ {
+ log(LL_ERR, "check_config: no local authentification secret in entry %d!", i);
+ error++;
+ }
+ }
+ }
+ if(error)
+ {
+ log(LL_ERR, "check_config: %d error(s) in configuration file, exit!", error);
+ do_exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * print the configuration
+ *---------------------------------------------------------------------------*/
+static void
+print_config(void)
+{
+#define PFILE stdout
+
+#ifdef I4B_EXTERNAL_MONITOR
+ extern struct monitor_rights * monitor_next_rights(const struct monitor_rights *r);
+ struct monitor_rights *m_rights;
+#endif
+ cfg_entry_t *cep = &cfg_entry_tab[0]; /* ptr to config entry */
+ int i, j;
+ time_t clock;
+ char mytime[64];
+
+ time(&clock);
+ strcpy(mytime, ctime(&clock));
+ mytime[strlen(mytime)-1] = '\0';
+
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "# system section (generated %s)\n", mytime);
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "system\n");
+ fprintf(PFILE, "useacctfile = %s\n", useacctfile ? "on\t\t\t\t# update accounting information file" : "off\t\t\t\t# don't update accounting information file");
+ fprintf(PFILE, "acctall = %s\n", acct_all ? "on\t\t\t\t# put all events into accounting file" : "off\t\t\t\t# put only charged events into accounting file");
+ fprintf(PFILE, "acctfile = %s\t\t# accounting information file\n", acctfile);
+ fprintf(PFILE, "ratesfile = %s\t\t# charging rates database file\n", ratesfile);
+
+#ifdef USE_RTPRIO
+ if(rt_prio == RTPRIO_NOTUSED)
+ fprintf(PFILE, "# rtprio is unused\n");
+ else
+ fprintf(PFILE, "rtprio = %d\t\t\t\t# isdnd runs at realtime priority\n", rt_prio);
+#endif
+
+ /* regular expression table */
+
+ for(i=0; i < MAX_RE; i++)
+ {
+ if(rarr[i].re_expr != NULL)
+ {
+ fprintf(PFILE, "regexpr = \"%s\"\t\t# scan logfile for this expression\n", rarr[i].re_expr);
+ }
+ if(rarr[i].re_prog != NULL)
+ {
+ fprintf(PFILE, "regprog = %s\t\t# program to run when expression is matched\n", rarr[i].re_prog);
+ }
+ }
+
+#ifdef I4B_EXTERNAL_MONITOR
+
+ fprintf(PFILE, "monitor-allowed = %s\n", do_monitor ? "on\t\t\t\t# remote isdnd monitoring allowed" : "off\t\t\t\t# remote isdnd monitoring disabled");
+ fprintf(PFILE, "monitor-port = %d\t\t\t\t# TCP/IP port number used for remote monitoring\n", monitorport);
+
+ m_rights = monitor_next_rights(NULL);
+ if(m_rights != NULL)
+ {
+ char *s = "error\n";
+ char b[512];
+
+ for ( ; m_rights != NULL; m_rights = monitor_next_rights(m_rights))
+ {
+ if(m_rights->local)
+ {
+ fprintf(PFILE, "monitor = \"%s\"\t\t# local socket name for monitoring\n", m_rights->name);
+ }
+ else
+ {
+ struct in_addr ia;
+ ia.s_addr = ntohl(m_rights->net);
+
+ switch(m_rights->mask)
+ {
+ case 0xffffffff:
+ s = "32";
+ break;
+ case 0xfffffffe:
+ s = "31";
+ break;
+ case 0xfffffffc:
+ s = "30";
+ break;
+ case 0xfffffff8:
+ s = "29";
+ break;
+ case 0xfffffff0:
+ s = "28";
+ break;
+ case 0xffffffe0:
+ s = "27";
+ break;
+ case 0xffffffc0:
+ s = "26";
+ break;
+ case 0xffffff80:
+ s = "25";
+ break;
+ case 0xffffff00:
+ s = "24";
+ break;
+ case 0xfffffe00:
+ s = "23";
+ break;
+ case 0xfffffc00:
+ s = "22";
+ break;
+ case 0xfffff800:
+ s = "21";
+ break;
+ case 0xfffff000:
+ s = "20";
+ break;
+ case 0xffffe000:
+ s = "19";
+ break;
+ case 0xffffc000:
+ s = "18";
+ break;
+ case 0xffff8000:
+ s = "17";
+ break;
+ case 0xffff0000:
+ s = "16";
+ break;
+ case 0xfffe0000:
+ s = "15";
+ break;
+ case 0xfffc0000:
+ s = "14";
+ break;
+ case 0xfff80000:
+ s = "13";
+ break;
+ case 0xfff00000:
+ s = "12";
+ break;
+ case 0xffe00000:
+ s = "11";
+ break;
+ case 0xffc00000:
+ s = "10";
+ break;
+ case 0xff800000:
+ s = "9";
+ break;
+ case 0xff000000:
+ s = "8";
+ break;
+ case 0xfe000000:
+ s = "7";
+ break;
+ case 0xfc000000:
+ s = "6";
+ break;
+ case 0xf8000000:
+ s = "5";
+ break;
+ case 0xf0000000:
+ s = "4";
+ break;
+ case 0xe0000000:
+ s = "3";
+ break;
+ case 0xc0000000:
+ s = "2";
+ break;
+ case 0x80000000:
+ s = "1";
+ break;
+ case 0x00000000:
+ s = "0";
+ break;
+ }
+ fprintf(PFILE, "monitor = \"%s/%s\"\t\t# host (net/mask) allowed to connect for monitoring\n", inet_ntoa(ia), s);
+ }
+ b[0] = '\0';
+
+ if((m_rights->rights) & I4B_CA_COMMAND_FULL)
+ strcat(b, "fullcmd,");
+ if((m_rights->rights) & I4B_CA_COMMAND_RESTRICTED)
+ strcat(b, "restrictedcmd,");
+ if((m_rights->rights) & I4B_CA_EVNT_CHANSTATE)
+ strcat(b, "channelstate,");
+ if((m_rights->rights) & I4B_CA_EVNT_CALLIN)
+ strcat(b, "callin,");
+ if((m_rights->rights) & I4B_CA_EVNT_CALLOUT)
+ strcat(b, "callout,");
+ if((m_rights->rights) & I4B_CA_EVNT_I4B)
+ strcat(b, "logevents,");
+
+ if(b[strlen(b)-1] == ',')
+ b[strlen(b)-1] = '\0';
+
+ fprintf(PFILE, "monitor-access = %s\t\t# monitor access rights\n", b);
+ }
+ }
+
+#endif
+ /* entry sections */
+
+ for(i=0; i <= entrycount; i++, cep++)
+ {
+ fprintf(PFILE, "\n");
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "# entry section %d\n", i);
+ fprintf(PFILE, "#---------------------------------------------------------------------------\n");
+ fprintf(PFILE, "entry\n");
+
+ fprintf(PFILE, "name = %s\t\t# name for this entry section\n", cep->name);
+
+ fprintf(PFILE, "isdncontroller = %d\t\t# ISDN card number used for this entry\n", cep->isdncontroller);
+ fprintf(PFILE, "isdnchannel = ");
+ switch(cep->isdnchannel)
+ {
+ case CHAN_ANY:
+ fprintf(PFILE, "-1\t\t# any ISDN B-channel may be used\n");
+ break;
+ default:
+ fprintf(PFILE, "%d\t\t# only ISDN B-channel %d may be used\n", cep->isdnchannel+1, cep->isdnchannel+1);
+ break;
+ }
+
+ fprintf(PFILE, "usrdevicename = %s\t\t# name of userland ISDN B-channel device\n", bdrivername(cep->usrdevicename));
+ fprintf(PFILE, "usrdeviceunit = %d\t\t# unit number of userland ISDN B-channel device\n", cep->usrdeviceunit);
+
+ fprintf(PFILE, "b1protocol = %s\n", cep->b1protocol ? "hdlc\t\t# B-channel layer 1 protocol is HDLC" : "raw\t\t# No B-channel layer 1 protocol used");
+
+ if(!(cep->usrdevicename == BDRV_TEL))
+ {
+ fprintf(PFILE, "direction = ");
+ switch(cep->inout)
+ {
+ case DIR_INONLY:
+ fprintf(PFILE, "in\t\t# only incoming connections allowed\n");
+ break;
+ case DIR_OUTONLY:
+ fprintf(PFILE, "out\t\t# only outgoing connections allowed\n");
+ break;
+ case DIR_INOUT:
+ fprintf(PFILE, "inout\t\t# incoming and outgoing connections allowed\n");
+ break;
+ }
+ }
+
+ if(!((cep->usrdevicename == BDRV_TEL) || (cep->inout == DIR_INONLY)))
+ {
+ if(cep->remote_numbers_count > 1)
+ {
+ for(j=0; j<cep->remote_numbers_count; j++)
+ fprintf(PFILE, "remote-phone-dialout = %s\t\t# telephone number %d for dialing out to remote\n", cep->remote_numbers[j].number, j+1);
+
+ fprintf(PFILE, "remdial-handling = ");
+
+ switch(cep->remote_numbers_handling)
+ {
+ case RNH_NEXT:
+ fprintf(PFILE, "next\t\t# use next number after last successfull for new dial\n");
+ break;
+ case RNH_LAST:
+ fprintf(PFILE, "last\t\t# use last successfull number for new dial\n");
+ break;
+ case RNH_FIRST:
+ fprintf(PFILE, "first\t\t# always start with first number for new dial\n");
+ break;
+ }
+ }
+ else
+ {
+ fprintf(PFILE, "remote-phone-dialout = %s\t\t# telephone number for dialing out to remote\n", cep->remote_numbers[0].number);
+ }
+
+ fprintf(PFILE, "local-phone-dialout = %s\t\t# show this number to remote when dialling out\n", cep->local_phone_dialout.number);
+ fprintf(PFILE, "dialout-type = %s\n", cep->dialouttype ? "calledback\t\t# i am called back by remote" : "normal\t\t# i am not called back by remote");
+ }
+
+ if(!(cep->inout == DIR_OUTONLY))
+ {
+ int n;
+
+ fprintf(PFILE, "local-phone-incoming = %s\t\t# incoming calls must match this (mine) telephone number\n", cep->local_phone_incoming.number);
+ for (n = 0; n < cep->incoming_numbers_count; n++)
+ fprintf(PFILE, "remote-phone-incoming = %s\t\t# this is a valid remote number to call me\n",
+ cep->remote_phone_incoming[n].number);
+
+ fprintf(PFILE, "dialin-reaction = ");
+ switch(cep->dialin_reaction)
+ {
+ case REACT_ACCEPT:
+ fprintf(PFILE, "accept\t\t# i accept a call from remote and connect\n");
+ break;
+ case REACT_REJECT:
+ fprintf(PFILE, "reject\t\t# i reject the call from remote\n");
+ break;
+ case REACT_IGNORE:
+ fprintf(PFILE, "ignore\t\t# i ignore the call from remote\n");
+ break;
+ case REACT_ANSWER:
+ fprintf(PFILE, "answer\t\t# i will start telephone answering when remote calls in\n");
+ break;
+ case REACT_CALLBACK:
+ fprintf(PFILE, "callback\t\t# when remote calls in, i will hangup and call back\n");
+ break;
+ }
+ }
+
+ if(cep->usrdevicename == BDRV_ISPPP)
+ {
+ char *s;
+ switch(cep->ppp_expect_auth)
+ {
+ case AUTH_NONE:
+ s = "none";
+ break;
+ case AUTH_PAP:
+ s = "pap";
+ break;
+ case AUTH_CHAP:
+ s = "chap";
+ break;
+ default:
+ s = NULL;
+ break;
+ }
+ if(s != NULL)
+ {
+ fprintf(PFILE, "ppp-expect-auth = %s\t\t# the auth protocol we expect to receive on dial-in (none,pap,chap)\n", s);
+ if(cep->ppp_expect_auth != AUTH_NONE)
+ {
+ fprintf(PFILE, "ppp-expect-name = %s\t\t# the user name allowed in\n", cep->ppp_expect_name);
+ fprintf(PFILE, "ppp-expect-password = %s\t\t# the key expected from the other side\n", cep->ppp_expect_password);
+ fprintf(PFILE, "ppp-auth-paranoid = %s\t\t# do we require remote to authenticate even if we dial out\n", cep->ppp_auth_flags & AUTH_REQUIRED ? "yes" : "no");
+ }
+ }
+ switch(cep->ppp_send_auth)
+ {
+ case AUTH_NONE:
+ s = "none";
+ break;
+ case AUTH_PAP:
+ s = "pap";
+ break;
+ case AUTH_CHAP:
+ s = "chap";
+ break;
+ default:
+ s = NULL;
+ break;
+ }
+ if(s != NULL)
+ {
+ fprintf(PFILE, "ppp-send-auth = %s\t\t# the auth protocol we use when dialing out (none,pap,chap)\n", s);
+ if(cep->ppp_send_auth != AUTH_NONE)
+ {
+ fprintf(PFILE, "ppp-send-name = %s\t\t# our PPP account used for dial-out\n", cep->ppp_send_name);
+ fprintf(PFILE, "ppp-send-password = %s\t\t# the key sent to the other side\n", cep->ppp_send_password);
+ }
+ }
+ if(cep->ppp_send_auth == AUTH_CHAP ||
+ cep->ppp_expect_auth == AUTH_CHAP) {
+ fprintf(PFILE, "ppp-auth-rechallenge = %s\t\t# rechallenge CHAP connections once in a while\n", cep->ppp_auth_flags & AUTH_RECHALLENGE ? "yes" : "no");
+ }
+ }
+
+ if(!((cep->inout == DIR_INONLY) || (cep->usrdevicename == BDRV_TEL)))
+ {
+ char *s;
+ fprintf(PFILE, "idletime-outgoing = %d\t\t# outgoing call idle timeout\n", cep->idle_time_out);
+
+ switch( cep->shorthold_algorithm )
+ {
+ case SHA_FIXU:
+ s = "fix-unit-size";
+ break;
+ case SHA_VARU:
+ s = "var-unit-size";
+ break;
+ default:
+ s = "error!!!";
+ break;
+ }
+
+ fprintf(PFILE, "idle-algorithm-outgoing = %s\t\t# outgoing call idle algorithm\n", s);
+ }
+
+ if(!(cep->inout == DIR_OUTONLY))
+ fprintf(PFILE, "idletime-incoming = %d\t\t# incoming call idle timeout\n", cep->idle_time_in);
+
+ if(!(cep->usrdevicename == BDRV_TEL))
+ {
+ fprintf(PFILE, "unitlengthsrc = ");
+ switch(cep->unitlengthsrc)
+ {
+ case ULSRC_NONE:
+ fprintf(PFILE, "none\t\t# no unit length specified, using default\n");
+ break;
+ case ULSRC_CMDL:
+ fprintf(PFILE, "cmdl\t\t# using unit length specified on commandline\n");
+ break;
+ case ULSRC_CONF:
+ fprintf(PFILE, "conf\t\t# using unitlength specified by unitlength-keyword\n");
+ fprintf(PFILE, "unitlength = %d\t\t# fixed unitlength\n", cep->unitlength);
+ break;
+ case ULSRC_RATE:
+ fprintf(PFILE, "rate\t\t# using unitlength specified in rate database\n");
+ fprintf(PFILE, "ratetype = %d\t\t# type of rate from rate database\n", cep->ratetype);
+ break;
+ case ULSRC_DYN:
+ fprintf(PFILE, "aocd\t\t# using dynamically calculated unitlength based on AOCD subscription\n");
+ fprintf(PFILE, "ratetype = %d\t\t# type of rate from rate database\n", cep->ratetype);
+ break;
+ }
+
+ fprintf(PFILE, "earlyhangup = %d\t\t# early hangup safety time\n", cep->earlyhangup);
+
+ }
+
+ if(cep->usrdevicename == BDRV_TEL)
+ {
+ fprintf(PFILE, "answerprog = %s\t\t# program used to answer incoming telephone calls\n", cep->answerprog);
+ fprintf(PFILE, "alert = %d\t\t# number of seconds to wait before accepting a call\n", cep->alert);
+ }
+
+ if(!(cep->usrdevicename == BDRV_TEL))
+ {
+ if(cep->dialin_reaction == REACT_CALLBACK)
+ fprintf(PFILE, "callbackwait = %d\t\t# i am waiting this time before calling back remote\n", cep->callbackwait);
+
+ if(cep->dialouttype == DIALOUT_CALLEDBACK)
+ fprintf(PFILE, "calledbackwait = %d\t\t# i am waiting this time for a call back from remote\n", cep->calledbackwait);
+
+ if(!(cep->inout == DIR_INONLY))
+ {
+ fprintf(PFILE, "dialretries = %d\t\t# number of dialing retries\n", cep->dialretries);
+ fprintf(PFILE, "recoverytime = %d\t\t# time to wait between dialling retries\n", cep->recoverytime);
+ fprintf(PFILE, "dialrandincr = %s\t\t# use random dialing time addon\n", cep->dialrandincr ? "on" : "off");
+
+ fprintf(PFILE, "usedown = %s\n", cep->usedown ? "on\t\t# ISDN device switched off on excessive dial failures" : "off\t\t# no device switchoff on excessive dial failures");
+ if(cep->usedown)
+ {
+ fprintf(PFILE, "downtries = %d\t\t# number of dialretries failures before switching off\n", cep->downtries);
+ fprintf(PFILE, "downtime = %d\t\t# time device is switched off\n", cep->downtime);
+ }
+ }
+ }
+ }
+ fprintf(PFILE, "\n");
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/rc_parse.y b/usr.sbin/i4b/isdnd/rc_parse.y
new file mode 100644
index 0000000..6427500
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_parse.y
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 1997 Joerg Wunsch. All rights reserved.
+ *
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - runtime configuration parser
+ * -----------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Aug 11 12:27:28 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+%{
+
+/* #define YYDEBUG 1 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "monitor.h" /* monitor access rights bit definitions */
+#include "isdnd.h"
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+extern void cfg_setval(int keyword);
+extern void cfg_set_controller_default();
+extern void reset_scanner(FILE *infile);
+extern void yyerror(const char *msg);
+extern int yylex();
+
+extern int lineno;
+extern char *yytext;
+extern int nentries;
+
+int saw_system = 0;
+int entrycount = -1;
+int controllercount = -1;
+
+%}
+
+%token ACCTALL
+%token ACCTFILE
+%token ADDPREFIX
+%token ALERT
+%token ALIASFNAME
+%token ALIASING
+%token ANSWERPROG
+%token B1PROTOCOL
+%token BEEPCONNECT
+%token BCAP
+%token BUDGETCALLOUTPERIOD
+%token BUDGETCALLOUTNCALLS
+%token BUDGETCALLOUTSFILE
+%token BUDGETCALLOUTSFILEROTATE
+%token BUDGETCALLBACKPERIOD
+%token BUDGETCALLBACKNCALLS
+%token BUDGETCALLBACKSFILE
+%token BUDGETCALLBACKSFILEROTATE
+%token CALLBACKWAIT
+%token CALLEDBACKWAIT
+%token CALLIN
+%token CALLOUT
+%token CHANNELSTATE
+%token CLONE
+%token CONNECTPROG
+%token CONTROLLER
+%token DIALOUTTYPE
+%token DIALRANDINCR
+%token DIALRETRIES
+%token DIRECTION
+%token DISCONNECTPROG
+%token DOWNTIME
+%token DOWNTRIES
+%token EARLYHANGUP
+%token ENTRY
+%token EXTCALLATTR
+%token FIRMWARE
+%token FULLCMD
+%token HOLIDAYFILE
+%token IDLETIME_IN
+%token IDLETIME_OUT
+%token IDLE_ALG_OUT
+%token ISDNCHANNEL
+%token ISDNCONTROLLER
+%token ISDNTIME
+%token ISDNTXDELIN
+%token ISDNTXDELOUT
+%token LOCAL_PHONE_DIALOUT
+%token LOCAL_SUBADDR_DIALOUT
+%token LOCAL_PHONE_INCOMING
+%token LOCAL_SUBADDR_INCOMING
+%token LOGEVENTS
+%token MAILER
+%token MAILTO
+%token MAXCONNECTTIME
+%token MONITOR
+%token MONITORACCESS
+%token MONITORPORT
+%token MONITORSW
+%token NAME
+%token NO
+%token OFF
+%token ON
+%token PPP_AUTH_RECHALLENGE
+%token PPP_AUTH_PARANOID
+%token PPP_EXPECT_AUTH
+%token PPP_EXPECT_NAME
+%token PPP_EXPECT_PASSWORD
+%token PPP_SEND_AUTH
+%token PPP_SEND_NAME
+%token PPP_SEND_PASSWORD
+%token PREFIXNATIONAL
+%token PREFIXINTERNATIONAL
+%token PROTOCOL
+%token RATESFILE
+%token RATETYPE
+%token REACTION
+%token RECOVERYTIME
+%token REGEXPR
+%token REGPROG
+%token REMOTE_NUMBERS_HANDLING
+%token REMOTE_PHONE_DIALOUT
+%token REMOTE_SUBADDR_DIALOUT
+%token REMOTE_PHONE_INCOMING
+%token REMOTE_SUBADDR_INCOMING
+%token RESTRICTEDCMD
+%token ROTATESUFFIX
+%token RTPRIO
+%token SYSTEM
+%token TINAINITPROG
+%token UNITLENGTH
+%token UNITLENGTHSRC
+%token USEACCTFILE
+%token USESUBADDR
+%token USEDOWN
+%token USRDEVICENAME
+%token USRDEVICEUNIT
+%token VALID
+%token YES
+
+
+%token <str> NUMBERSTR
+
+%token <str> STRING
+
+%type <booln> boolean
+
+%type <num> sysfilekeyword sysnumkeyword sysstrkeyword sysboolkeyword
+%type <num> filekeyword numkeyword strkeyword boolkeyword monrights monright
+%type <num> cstrkeyword cfilekeyword
+%type <str> filename
+
+%union {
+ int booln;
+ int num;
+ char *str;
+}
+
+%%
+
+config: sections
+ ;
+
+sections: possible_nullentries
+ syssect
+ optcontrollersects
+ entrysects
+ ;
+
+possible_nullentries:
+ /* lambda */
+ | possible_nullentries error '\n'
+ | possible_nullentries nullentry
+ ;
+
+nullentry: '\n'
+ ;
+
+entrysects: entrysect
+ | entrysects entrysect
+ ;
+
+optcontrollersects:
+ controllersects
+ |
+ {
+ cfg_set_controller_default();
+ }
+ ;
+
+controllersects: controllersect
+ | controllersects controllersect
+ ;
+
+/* ============== */
+/* system section */
+/* ============== */
+
+syssect: SYSTEM sysentries
+ ;
+
+sysentries: sysentry
+ {
+ saw_system = 1;
+ monitor_clear_rights();
+ }
+ | sysentries sysentry
+ ;
+
+sysentry: sysfileentry
+ | sysboolentry
+ | sysnumentry
+ | sysstrentry
+ | sysmonitorstart
+ | sysmonitorrights
+ | nullentry
+ | error '\n'
+ ;
+
+
+sysmonitorstart:
+ MONITOR '=' STRING '\n'
+ {
+ char *err = NULL;
+ switch (monitor_start_rights($3)) {
+ case I4BMAR_OK:
+ break;
+ case I4BMAR_LENGTH:
+ err = "local socket name too long: %s";
+ break;
+ case I4BMAR_DUP:
+ err = "duplicate entry: %s";
+ break;
+ case I4BMAR_CIDR:
+ err = "invalid CIDR specification: %s";
+ break;
+ case I4BMAR_NOIP:
+ err = "could not resolve host or net specification: %s";
+ break;
+ }
+ if (err) {
+ char msg[1024];
+ snprintf(msg, sizeof msg, err, $3);
+ yyerror(msg);
+ }
+ }
+ ;
+
+sysmonitorrights:
+ MONITORACCESS '=' monrights '\n'
+ { monitor_add_rights($3); }
+ ;
+
+monrights: monrights ',' monright { $$ = $1 | $3; }
+ | monright { $$ = $1; }
+ ;
+
+monright: FULLCMD { $$ = I4B_CA_COMMAND_FULL; }
+ | RESTRICTEDCMD { $$ = I4B_CA_COMMAND_RESTRICTED; }
+ | CHANNELSTATE { $$ = I4B_CA_EVNT_CHANSTATE; }
+ | CALLIN { $$ = I4B_CA_EVNT_CALLIN; }
+ | CALLOUT { $$ = I4B_CA_EVNT_CALLOUT; }
+ | LOGEVENTS { $$ = I4B_CA_EVNT_I4B; }
+ ;
+
+sysfileentry: sysfilekeyword '=' filename '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+sysboolentry: sysboolkeyword '=' boolean '\n'
+ {
+ yylval.booln = $3;
+ cfg_setval($1);
+ }
+ ;
+
+sysnumentry: sysnumkeyword '=' NUMBERSTR '\n'
+ {
+ yylval.num = atoi($3);
+ cfg_setval($1);
+ }
+ ;
+
+sysstrentry: sysstrkeyword '=' STRING '\n'
+ {
+ cfg_setval($1);
+ }
+ | sysstrkeyword '=' NUMBERSTR '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+filename: STRING {
+ if ($1[0] != '/')
+ {
+ yyerror("filename doesn't start with a slash");
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+boolean: NO { $$ = FALSE; }
+ | OFF { $$ = FALSE; }
+ | ON { $$ = TRUE; }
+ | YES { $$ = TRUE; }
+ ;
+
+sysfilekeyword: RATESFILE { $$ = RATESFILE; }
+ | ACCTFILE { $$ = ACCTFILE; }
+ | ALIASFNAME { $$ = ALIASFNAME; }
+ | HOLIDAYFILE { $$ = HOLIDAYFILE; }
+ | TINAINITPROG { $$ = TINAINITPROG; }
+ ;
+
+sysboolkeyword: USEACCTFILE { $$ = USEACCTFILE; }
+ | ALIASING { $$ = ALIASING; }
+ | ACCTALL { $$ = ACCTALL; }
+ | ADDPREFIX { $$ = ADDPREFIX; }
+ | BEEPCONNECT { $$ = BEEPCONNECT; }
+ | EXTCALLATTR { $$ = EXTCALLATTR; }
+ | ISDNTIME { $$ = ISDNTIME; }
+ | MONITORSW { $$ = MONITORSW; }
+ ;
+
+sysnumkeyword: MONITORPORT { $$ = MONITORPORT; }
+ | RTPRIO { $$ = RTPRIO; }
+ ;
+
+sysstrkeyword: MAILER { $$ = MAILER; }
+ | MAILTO { $$ = MAILTO; }
+ | PREFIXNATIONAL { $$ = PREFIXNATIONAL; }
+ | PREFIXINTERNATIONAL { $$ = PREFIXINTERNATIONAL; }
+ | ROTATESUFFIX { $$ = ROTATESUFFIX; }
+ | REGEXPR { $$ = REGEXPR; }
+ | REGPROG { $$ = REGPROG; }
+ ;
+
+/* ============= */
+/* entry section */
+/* ============= */
+
+entrysect: ENTRY
+ {
+ entrycount++;
+ nentries++;
+ }
+ entries
+ ;
+
+entries: entry
+ | entries entry
+ ;
+
+entry: fileentry
+ | strentry
+ | numentry
+ | boolentry
+ | nullentry
+ | error '\n'
+ ;
+
+fileentry: filekeyword '=' filename '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+
+strentry: strkeyword '=' STRING '\n'
+ {
+ cfg_setval($1);
+ }
+ | strkeyword '=' NUMBERSTR '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+boolentry: boolkeyword '=' boolean '\n'
+ {
+ yylval.booln = $3;
+ cfg_setval($1);
+ }
+ ;
+
+numentry: numkeyword '=' NUMBERSTR '\n'
+ {
+ yylval.num = atoi($3);
+ cfg_setval($1);
+ }
+ ;
+
+filekeyword: BUDGETCALLBACKSFILE { $$ = BUDGETCALLBACKSFILE; }
+ | BUDGETCALLOUTSFILE { $$ = BUDGETCALLOUTSFILE; }
+ ;
+
+strkeyword: ANSWERPROG { $$ = ANSWERPROG; }
+ | B1PROTOCOL { $$ = B1PROTOCOL; }
+ | BCAP { $$ = BCAP; }
+ | CONNECTPROG { $$ = CONNECTPROG; }
+ | DIALOUTTYPE { $$ = DIALOUTTYPE; }
+ | DIRECTION { $$ = DIRECTION; }
+ | DISCONNECTPROG { $$ = DISCONNECTPROG; }
+ | IDLE_ALG_OUT { $$ = IDLE_ALG_OUT; }
+ | LOCAL_PHONE_INCOMING { $$ = LOCAL_PHONE_INCOMING; }
+ | LOCAL_SUBADDR_INCOMING { $$ = LOCAL_SUBADDR_INCOMING; }
+ | LOCAL_PHONE_DIALOUT { $$ = LOCAL_PHONE_DIALOUT; }
+ | LOCAL_SUBADDR_DIALOUT { $$ = LOCAL_SUBADDR_DIALOUT; }
+ | NAME { $$ = NAME; }
+ | PPP_EXPECT_AUTH { $$ = PPP_EXPECT_AUTH; }
+ | PPP_EXPECT_NAME { $$ = PPP_EXPECT_NAME; }
+ | PPP_EXPECT_PASSWORD { $$ = PPP_EXPECT_PASSWORD; }
+ | PPP_SEND_AUTH { $$ = PPP_SEND_AUTH; }
+ | PPP_SEND_NAME { $$ = PPP_SEND_NAME; }
+ | PPP_SEND_PASSWORD { $$ = PPP_SEND_PASSWORD; }
+ | REACTION { $$ = REACTION; }
+ | REMOTE_NUMBERS_HANDLING { $$ = REMOTE_NUMBERS_HANDLING; }
+ | REMOTE_PHONE_INCOMING { $$ = REMOTE_PHONE_INCOMING; }
+ | REMOTE_SUBADDR_INCOMING { $$ = REMOTE_SUBADDR_INCOMING; }
+ | REMOTE_PHONE_DIALOUT { $$ = REMOTE_PHONE_DIALOUT; }
+ | REMOTE_SUBADDR_DIALOUT { $$ = REMOTE_SUBADDR_DIALOUT; }
+ | UNITLENGTHSRC { $$ = UNITLENGTHSRC; }
+ | USRDEVICENAME { $$ = USRDEVICENAME; }
+ | VALID { $$ = VALID; }
+ | CLONE { $$ = CLONE; }
+ ;
+
+numkeyword: ALERT { $$ = ALERT; }
+ | BUDGETCALLBACKPERIOD { $$ = BUDGETCALLBACKPERIOD; }
+ | BUDGETCALLBACKNCALLS { $$ = BUDGETCALLBACKNCALLS; }
+ | BUDGETCALLOUTPERIOD { $$ = BUDGETCALLOUTPERIOD; }
+ | BUDGETCALLOUTNCALLS { $$ = BUDGETCALLOUTNCALLS; }
+ | CALLBACKWAIT { $$ = CALLBACKWAIT; }
+ | CALLEDBACKWAIT { $$ = CALLEDBACKWAIT; }
+ | DIALRETRIES { $$ = DIALRETRIES; }
+ | EARLYHANGUP { $$ = EARLYHANGUP; }
+ | IDLETIME_IN { $$ = IDLETIME_IN; }
+ | IDLETIME_OUT { $$ = IDLETIME_OUT; }
+ | ISDNCONTROLLER { $$ = ISDNCONTROLLER; }
+ | ISDNCHANNEL { $$ = ISDNCHANNEL; }
+ | ISDNTXDELIN { $$ = ISDNTXDELIN; }
+ | ISDNTXDELOUT { $$ = ISDNTXDELOUT; }
+ | RATETYPE { $$ = RATETYPE; }
+ | RECOVERYTIME { $$ = RECOVERYTIME; }
+ | UNITLENGTH { $$ = UNITLENGTH; }
+ | USRDEVICEUNIT { $$ = USRDEVICEUNIT; }
+ | DOWNTIME { $$ = DOWNTIME; }
+ | DOWNTRIES { $$ = DOWNTRIES; }
+ | MAXCONNECTTIME { $$ = MAXCONNECTTIME; }
+ ;
+
+boolkeyword: BUDGETCALLBACKSFILEROTATE { $$ = BUDGETCALLBACKSFILEROTATE; }
+ | BUDGETCALLOUTSFILEROTATE { $$ = BUDGETCALLOUTSFILEROTATE; }
+ | DIALRANDINCR { $$ = DIALRANDINCR; }
+ | PPP_AUTH_RECHALLENGE { $$ = PPP_AUTH_RECHALLENGE; }
+ | PPP_AUTH_PARANOID { $$ = PPP_AUTH_PARANOID; }
+ | USEDOWN { $$ = USEDOWN; }
+ | USESUBADDR { $$ = USESUBADDR; }
+ ;
+
+/* ================== */
+/* controller section */
+/* ================== */
+
+controllersect: CONTROLLER
+ {
+ controllercount++;
+ }
+ controllers
+ ;
+
+controllers: controller
+ | controllers controller
+ ;
+
+controller: strcontroller
+ | nullentry
+ | error '\n'
+ ;
+
+strcontroller: cstrkeyword '=' STRING '\n'
+ {
+ cfg_setval($1);
+ }
+ | cstrkeyword '=' NUMBERSTR '\n'
+ {
+ cfg_setval($1);
+ }
+ | cfilekeyword '=' filename '\n'
+ {
+ cfg_setval($1);
+ }
+ ;
+
+cstrkeyword: PROTOCOL { $$ = PROTOCOL; }
+ ;
+
+cfilekeyword: FIRMWARE { $$ = FIRMWARE; }
+ ;
+
+
+%%
diff --git a/usr.sbin/i4b/isdnd/rc_scan.l b/usr.sbin/i4b/isdnd/rc_scan.l
new file mode 100644
index 0000000..988a4fb
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_scan.l
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1997 Joerg Wunsch. All rights reserved.
+ *
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - runtime configuration lexical analyzer
+ * ---------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Aug 11 12:27:50 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+%{
+
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sysexits.h>
+
+#include "y.tab.h"
+
+int lineno;
+
+%}
+
+%option noyywrap
+%option nounput
+
+%%
+
+#.*$ { /*
+ * Drop comment. NB: this prevents a hash
+ * sign from appearing inside a quoted string.
+ */
+ }
+
+["][^"]*["] {
+ char *str;
+ if ((str = malloc(yyleng - 1)) == 0)
+ errx(EX_OSERR, "Out of virtual memory");
+ memcpy(str, yytext + 1, yyleng - 2);
+ str[yyleng - 2] = 0;
+ yylval.str = str;
+ return STRING;
+ }
+
+(-*[0-9]+)|\* {
+ char *str;
+ char *p = yytext;
+ int i = 0;
+ if ((str = malloc(128)) == 0)
+ errx(EX_OSERR, "Out of virtual memory");
+ while(*p == '-' || isdigit(*p) || *p == '*')
+ str[i++] = *p++;
+ str[i] = '\0';
+ yylval.str = str;
+ return NUMBERSTR;
+ }
+
+acctall { return ACCTALL; }
+acctfile { return ACCTFILE; }
+add-prefix { return ADDPREFIX; }
+alert { return ALERT; }
+aliasing { return ALIASING; }
+aliasfile { return ALIASFNAME; }
+answerprog { return ANSWERPROG; }
+b1protocol { return B1PROTOCOL; }
+bcap { return BCAP; }
+beepconnect { return BEEPCONNECT; }
+budget-callbackperiod { return BUDGETCALLBACKPERIOD; }
+budget-callbackncalls { return BUDGETCALLBACKNCALLS; }
+budget-callbacksfile { return BUDGETCALLBACKSFILE; }
+budget-callbacksfile-rotate { return BUDGETCALLBACKSFILEROTATE; }
+budget-calloutperiod { return BUDGETCALLOUTPERIOD; }
+budget-calloutncalls { return BUDGETCALLOUTNCALLS; }
+budget-calloutsfile { return BUDGETCALLOUTSFILE; }
+budget-calloutsfile-rotate { return BUDGETCALLOUTSFILEROTATE; }
+callbackwait { return CALLBACKWAIT; }
+calledbackwait { return CALLEDBACKWAIT; }
+clone { return CLONE; }
+connectprog { return CONNECTPROG; }
+controller { return CONTROLLER; }
+dialin-reaction { return REACTION; }
+dialout-type { return DIALOUTTYPE; }
+dialrandincr { return DIALRANDINCR; }
+dialretries { return DIALRETRIES; }
+direction { return DIRECTION; }
+disconnectprog { return DISCONNECTPROG; }
+downtries { return DOWNTRIES; }
+downtime { return DOWNTIME; }
+earlyhangup { return EARLYHANGUP; }
+entry { return ENTRY; }
+extcallattr { return EXTCALLATTR; }
+firmware { return FIRMWARE; }
+holidayfile { return HOLIDAYFILE; }
+idletime-incoming { return IDLETIME_IN; }
+idletime-outgoing { return IDLETIME_OUT; }
+idle-algorithm-outgoing { return IDLE_ALG_OUT; }
+isdncontroller { return ISDNCONTROLLER; }
+isdnchannel { return ISDNCHANNEL; }
+isdntime { return ISDNTIME; }
+isdntxdel-incoming { return ISDNTXDELIN; }
+isdntxdel-outgoing { return ISDNTXDELOUT; }
+local-phone-dialout { return LOCAL_PHONE_DIALOUT; }
+local-subaddr-dialout { return LOCAL_SUBADDR_DIALOUT; }
+local-phone-incoming { return LOCAL_PHONE_INCOMING; }
+local-subaddr-incoming { return LOCAL_SUBADDR_INCOMING; }
+mailer { return MAILER; }
+mailto { return MAILTO; }
+maxconnecttime { return MAXCONNECTTIME; }
+monitor-allowed { return MONITORSW; }
+monitor-port { return MONITORPORT; }
+monitor { return MONITOR; }
+monitor-access { return MONITORACCESS; }
+fullcmd { return FULLCMD; }
+restrictedcmd { return RESTRICTEDCMD; }
+channelstate { return CHANNELSTATE; }
+callin { return CALLIN; }
+callout { return CALLOUT; }
+logevents { return LOGEVENTS; }
+name { return NAME; }
+no { return NO; }
+off { return OFF; }
+on { return ON; }
+ppp-auth-rechallenge { return PPP_AUTH_RECHALLENGE; }
+ppp-auth-paranoid { return PPP_AUTH_PARANOID; }
+ppp-expect-auth { return PPP_EXPECT_AUTH; }
+ppp-expect-name { return PPP_EXPECT_NAME; }
+ppp-expect-password { return PPP_EXPECT_PASSWORD; }
+ppp-send-auth { return PPP_SEND_AUTH; }
+ppp-send-name { return PPP_SEND_NAME; }
+ppp-send-password { return PPP_SEND_PASSWORD; }
+prefix-international { return PREFIXINTERNATIONAL; }
+prefix-national { return PREFIXNATIONAL; }
+protocol { return PROTOCOL; }
+ratesfile { return RATESFILE; }
+ratetype { return RATETYPE; }
+recoverytime { return RECOVERYTIME; }
+regexpr { return REGEXPR; }
+regprog { return REGPROG; }
+remdial-handling { return REMOTE_NUMBERS_HANDLING; }
+remote-phone-dialout { return REMOTE_PHONE_DIALOUT; }
+remote-subaddr-dialout { return REMOTE_SUBADDR_DIALOUT; }
+remote-phone-incoming { return REMOTE_PHONE_INCOMING; }
+remote-subaddr-incoming { return REMOTE_SUBADDR_INCOMING; }
+rotatesuffix { return ROTATESUFFIX; }
+rtprio { return RTPRIO; }
+system { return SYSTEM; }
+tinainitprog { return TINAINITPROG; }
+unitlength { return UNITLENGTH; }
+unitlengthsrc { return UNITLENGTHSRC; }
+useacctfile { return USEACCTFILE; }
+usesubaddr { return USESUBADDR; }
+usrdevicename { return USRDEVICENAME; }
+usrdeviceunit { return USRDEVICEUNIT; }
+usedown { return USEDOWN; }
+valid { return VALID; }
+yes { return YES; }
+
+\n { lineno++; return '\n'; }
+
+[A-Za-z/.][-A-Za-z0-9_/.]* {
+ char *str;
+ if ((str = strdup(yytext)) == 0)
+ err(EX_OSERR, "Out of virtual memory");
+ yylval.str = str;
+ return STRING;
+ }
+
+[ \t] { /* drop white space */ }
+
+. { return yytext[0]; }
+
+%%
+
+void
+reset_scanner(FILE *infile)
+{
+ yyrestart(infile);
+ lineno = 1;
+}
diff --git a/usr.sbin/i4b/isdnd/support.c b/usr.sbin/i4b/isdnd/support.c
new file mode 100644
index 0000000..337c215
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/support.c
@@ -0,0 +1,1169 @@
+/*
+ * Copyright (c) 1997, 2003 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - misc support routines
+ * ----------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Thu Jul 31 11:05:16 2003]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static int isvalidtime(cfg_entry_t *cep);
+
+/*---------------------------------------------------------------------------*
+ * find an active entry by driver type and driver unit
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_active_entry_by_driver(int drivertype, int driverunit)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "find_active_entry_by_driver: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found */
+
+ if(cep->cdid == CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_UNUSED !",
+ i, bdrivername(drivertype), driverunit)));
+ return(NULL);
+ }
+ else if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_RESERVED!",
+ i, bdrivername(drivertype), driverunit)));
+ return(NULL);
+ }
+ return(cep);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for dialing out
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_by_device_for_dialout(int drivertype, int driverunit)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* compare driver type and unit */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found, check if already reserved */
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, cdid reserved!", i)));
+ return(NULL);
+ }
+
+ /* check if this entry is already in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, cdid in use", i)));
+ return(NULL);
+ }
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ /* found an entry to be used for calling out */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: found entry %d!", i)));
+ return(cep);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, setup_dialout() failed!", i)));
+ return(NULL);
+ }
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: no entry found!")));
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for dialing out
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_by_device_for_dialoutnumber(msg_dialoutnumber_ind_t *mp)
+{
+ cfg_entry_t *cep = NULL;
+ int i, j;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* compare driver type and unit */
+
+ if(!((cep->usrdevicename == mp->driver) &&
+ (cep->usrdeviceunit == mp->driver_unit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found, check if already reserved */
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid reserved!", i)));
+ return(NULL);
+ }
+
+ /* check if this entry is already in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid in use", i)));
+ return(NULL);
+ }
+
+ cep->keypad[0] = '\0';
+
+ /* check number and copy to cep->remote_numbers[] */
+
+ for(j = 0; j < mp->cmdlen; j++)
+ {
+ if(!(isdigit(*(mp->cmd+j))))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, dial string contains non-digit at pos %d", i, j)));
+ return(NULL);
+ }
+ /* fill in number to dial */
+ cep->remote_numbers[0].number[j] = *(mp->cmd+j);
+ }
+ cep->remote_numbers[0].number[j] = '\0';
+
+/* XXX subaddr does not have to be a digit! isgraph() would be a better idea */
+ for(j = 0; j < mp->subaddrlen; j++)
+ {
+ if(!(isdigit(*(mp->subaddr+j))))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, subaddr string contains non-digit at pos %d", i, j)));
+ return(NULL);
+ }
+ /* fill in number to dial */
+ cep->remote_numbers[0].subaddr[j] = *(mp->subaddr+j);
+ }
+ cep->remote_numbers[0].subaddr[j] = '\0';
+
+ cep->remote_numbers_count = 1;
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ /* found an entry to be used for calling out */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: found entry %d!", i)));
+ return(cep);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, setup_dialout() failed!", i)));
+ return(NULL);
+ }
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: no entry found!")));
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for send keypad
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_by_device_for_keypad(int drivertype, int driverunit, int cmdlen, char *cmd)
+{
+ cfg_entry_t *cep = NULL;
+ int i, j;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* compare driver type and unit */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found, check if already reserved */
+
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, cdid reserved!", i)));
+ return(NULL);
+ }
+
+ /* check if this entry is already in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, cdid in use", i)));
+ return(NULL);
+ }
+
+ cep->remote_numbers[0].number[0] = '\0';
+ cep->remote_numbers_count = 0;
+ cep->remote_phone_dialout.number[0] = '\0';
+
+ bzero(cep->keypad, KEYPAD_MAX);
+ strncpy(cep->keypad, cmd, cmdlen);
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, keypad string is %s", i, cep->keypad)));
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ /* found an entry to be used for calling out */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: found entry %d!", i)));
+ return(cep);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, setup_dialout() failed!", i)));
+ return(NULL);
+ }
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: no entry found!")));
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit and setup for dialing out
+ *---------------------------------------------------------------------------*/
+int
+setup_dialout(cfg_entry_t *cep)
+{
+ int i;
+
+ /* check controller operational */
+
+ if((get_controller_state(cep->isdncontroller)) != CTRL_UP)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, controller is down", cep->name)));
+ return(ERROR);
+ }
+
+ cep->isdncontrollerused = cep->isdncontroller;
+
+ /* check channel available */
+
+ switch(cep->isdnchannel)
+ {
+ case CHAN_ANY:
+ for (i = 0; i < isdn_ctrl_tab[cep->isdncontroller].nbch; i++)
+ {
+ if(ret_channel_state(cep->isdncontroller, i) == CHAN_IDLE)
+ break;
+ }
+
+ if (i == isdn_ctrl_tab[cep->isdncontroller].nbch)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, no channel free", cep->name)));
+ return(ERROR);
+ }
+ cep->isdnchannelused = CHAN_ANY;
+ break;
+
+ default:
+ if((ret_channel_state(cep->isdncontroller, cep->isdnchannel)) != CHAN_IDLE)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, channel not free", cep->name)));
+ return(ERROR);
+ }
+ cep->isdnchannelused = cep->isdnchannel;
+ break;
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s ok!", cep->name)));
+
+ /* preset disconnect cause */
+
+ SET_CAUSE_TYPE(cep->disc_cause, CAUSET_I4B);
+ SET_CAUSE_VAL(cep->disc_cause, CAUSE_I4B_NORMAL);
+
+ return(GOOD);
+}
+
+/*---------------------------------------------------------------------------*
+ * find entry by drivertype and driverunit
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+get_cep_by_driver(int drivertype, int driverunit)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(!((cep->usrdevicename == drivertype) &&
+ (cep->usrdeviceunit == driverunit)))
+ {
+ continue;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "get_cep_by_driver: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ DBGL(DL_MSG, (log(LL_DBG, "get_cep_by_driver: found entry %d!", i)));
+ return(cep);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * find a matching entry for an incoming call
+ *
+ * - not found/no match: log output with LL_CHD and return NULL
+ * - found/match: make entry in free cep, return address
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+find_matching_entry_incoming(msg_connect_ind_t *mp)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+
+ /* check for CW (call waiting) early */
+
+ if(mp->channel == CHAN_NO)
+ {
+ if(aliasing)
+ {
+ char *src_tela = "ERROR-src_tela";
+ char *dst_tela = "ERROR-dst_tela";
+
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+
+ log(LL_CHD, "%05d <unknown> CW from %s to %s (no channel free)",
+ mp->header.cdid, src_tela, dst_tela);
+ }
+ else
+ {
+ log(LL_CHD, "%05d <unknown> call waiting from %s to %s (no channel free)",
+ mp->header.cdid, mp->src_telno, mp->dst_telno);
+ }
+ return(NULL);
+ }
+
+ for(i=0; i < nentries; i++)
+ {
+ int n;
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ /* check my number */
+
+ if(strncmp(cep->local_phone_incoming.number, mp->dst_telno, strlen(cep->local_phone_incoming.number)))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, myno %s != incomingno %s", i,
+ cep->local_phone_incoming.number, mp->dst_telno)));
+ continue;
+ }
+
+ if (cep->usesubaddr && strncmp(cep->local_phone_incoming.subaddr, mp->dst_subaddr, strlen(cep->local_phone_incoming.subaddr)))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, mysubno %s != incomingsubno %s", i,
+ cep->local_phone_incoming.subaddr, mp->dst_subaddr)));
+ continue;
+ }
+
+ /* check all allowed remote numbers for this entry */
+
+ for (n = 0; n < cep->incoming_numbers_count; n++)
+ {
+ incoming_number_t *in = &cep->remote_phone_incoming[n];
+
+ /*
+ * An incoming number matches whenever the main
+ * phone number either matches or is a wildcard AND
+ * subaddresses are either not in use or match as
+ * well (or the required subaddress is a wildcard).
+ * This means that if subaddresses are in use and
+ * the main phone number is a wildcard, the
+ * subaddress is still required to match.
+ *
+ * At first glance, this does not seem logical,
+ * but since subaddress usage can be configured per
+ * entry, disregarding the subaddress if the main
+ * number matches would needlessly limit the user's
+ * flexibility.
+ */
+
+ if ((in->number[0] == '*') || (!strncmp(in->number, mp->src_telno, strlen(in->number))))
+ {
+ if ((!cep->usesubaddr) || (in->subaddr[0] == '*') || (!strncmp(in->subaddr, mp->src_subaddr, strlen(in->subaddr))))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, match: remno %s = incomingfromno %s", i,
+ in->number, mp->src_telno)));
+ break;
+ }
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s", i,
+ in->number, mp->src_telno)));
+ }
+ }
+
+ /* If all configured remote numbers have been tested without success, proceed to the next entry. */
+ if (n >= cep->incoming_numbers_count)
+ continue;
+
+ /* check b protocol */
+
+ if(cep->b1protocol != mp->bprot)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, bprot %d != incomingprot %d", i,
+ cep->b1protocol, mp->bprot)));
+ continue;
+ }
+
+ /* is this entry currently in use ? */
+
+ if(cep->cdid != CDID_UNUSED)
+ {
+ if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, cdid is reserved", i)));
+ }
+ else if (cep->dialin_reaction == REACT_ACCEPT
+ && cep->dialouttype == DIALOUT_CALLEDBACK)
+ {
+ /*
+ * We might consider doing this even if this is
+ * not a calledback config entry - BUT: there are
+ * severe race conditions and timinig problems
+ * ex. if both sides run I4B with no callback
+ * delay - both may shutdown the outgoing call
+ * and never be able to establish a connection.
+ * In the called-back case this should not happen.
+ */
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, incoming call for callback in progress (cdid %05d)", i, cep->cdid)));
+
+ /* save the current call state, we're going to overwrite it with the
+ * new incoming state below... */
+ cep->saved_call.cdid = cep->cdid;
+ cep->saved_call.controller = cep->isdncontrollerused;
+ cep->saved_call.channel = cep->isdnchannelused;
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, cdid in use", i)));
+ continue; /* yes, next */
+ }
+ }
+
+ /* check controller value ok */
+
+ if(mp->controller > ncontroller)
+ {
+ log(LL_CHD, "%05d %s incoming call with invalid controller %d",
+ mp->header.cdid, cep->name, mp->controller);
+ return(NULL);
+ }
+
+ /* check controller marked up */
+
+ if((get_controller_state(mp->controller)) != CTRL_UP)
+ {
+ log(LL_CHD, "%05d %s incoming call, controller %d DOWN!",
+ mp->header.cdid, cep->name, mp->controller);
+ return(NULL);
+ }
+
+ /*
+ * check controller he wants, check for any
+ * controller or specific controller
+ */
+
+ if( (mp->controller != -1) &&
+ (mp->controller != cep->isdncontroller) )
+ {
+ log(LL_CHD, "%05d %s incoming call, controller %d != incoming %d",
+ mp->header.cdid, cep->name,
+ cep->isdncontroller, mp->controller);
+ continue;
+ }
+
+ /* check channel he wants */
+
+ switch(mp->channel)
+ {
+ case CHAN_ANY:
+ for (i = 0; i < isdn_ctrl_tab[mp->controller].nbch; i++)
+ {
+ if(ret_channel_state(mp->controller, i) == CHAN_IDLE)
+ break;
+ }
+
+ if (i == isdn_ctrl_tab[mp->controller].nbch)
+ {
+ log(LL_CHD, "%05d %s incoming call, no channel free!",
+ mp->header.cdid, cep->name);
+ return(NULL);
+ }
+ break;
+
+ case CHAN_NO:
+ log(LL_CHD, "%05d %s incoming call, call waiting (no channel available)!",
+ mp->header.cdid, cep->name);
+ return(NULL);
+ break;
+
+ default:
+ if((ret_channel_state(mp->controller, mp->channel)) != CHAN_IDLE)
+ {
+ log(LL_CHD, "%05d %s incoming call, channel B%d not free!",
+ mp->header.cdid, cep->name, mp->channel+1);
+ return(NULL);
+ }
+ break;
+ }
+
+ /* check time interval */
+
+ if(isvalidtime(cep) == 0)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, time not valid!", i)));
+ continue;
+ }
+
+ /* found a matching entry */
+
+ cep->cdid = mp->header.cdid;
+ cep->isdncontrollerused = mp->controller;
+ cep->isdnchannelused = mp->channel;
+/*XXX*/ cep->disc_cause = 0;
+
+ /* cp number to real one used */
+
+ strcpy(cep->real_phone_incoming.number, mp->src_telno);
+
+ /* copy display string */
+
+ strcpy(cep->display, mp->display);
+
+ /* entry currently down ? */
+
+ if(cep->state == ST_DOWN)
+ {
+ msg_updown_ind_t mui;
+
+ /* set interface up */
+
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, ", i)));
+
+ mui.driver = cep->usrdevicename;
+ mui.driver_unit = cep->usrdeviceunit;
+ mui.updown = SOFT_ENA;
+
+ if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
+ {
+ log(LL_ERR, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ error_exit(1, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ }
+
+ cep->down_retry_count = 0;
+ cep->state = ST_IDLE;
+ }
+ return(cep);
+ }
+
+ if(aliasing)
+ {
+ char *src_tela = "ERROR-src_tela";
+ char *dst_tela = "ERROR-dst_tela";
+
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+
+ log(LL_CHD, "%05d Call from %s to %s",
+ mp->header.cdid, src_tela, dst_tela);
+ }
+ else
+ {
+ log(LL_CHD, "%05d <unknown> incoming call from %s to %s ctrl %d",
+ mp->header.cdid, mp->src_telno, mp->dst_telno, mp->controller);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * return address of ACTIVE config entry by controller and channel
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+get_cep_by_cc(int ctrlr, int chan)
+{
+ int i;
+
+ if((chan < 0) || (chan >= isdn_ctrl_tab[ctrlr].nbch))
+ return(NULL);
+
+ for(i=0; i < nentries; i++)
+ {
+ if((cfg_entry_tab[i].cdid != CDID_UNUSED) &&
+ (cfg_entry_tab[i].cdid != CDID_RESERVED) &&
+ (cfg_entry_tab[i].isdnchannelused == chan) &&
+ (cfg_entry_tab[i].isdncontrollerused == ctrlr) &&
+ ((ret_channel_state(ctrlr, chan)) == CHAN_RUN))
+ {
+ return(&cfg_entry_tab[i]);
+ }
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * return address of config entry identified by cdid
+ *---------------------------------------------------------------------------*/
+cfg_entry_t *
+get_cep_by_cdid(int cdid)
+{
+ int i;
+
+ for(i=0; i < nentries; i++)
+ {
+ if(cfg_entry_tab[i].cdid == cdid
+ || cfg_entry_tab[i].saved_call.cdid == cdid)
+ return(&cfg_entry_tab[i]);
+ }
+ return(NULL);
+}
+
+/*---------------------------------------------------------------------------*
+ * return b channel driver type name string
+ *---------------------------------------------------------------------------*/
+char *
+bdrivername(int drivertype)
+{
+ static char *bdtab[] = {
+ "rbch",
+ "tel",
+ "ipr",
+ "isp",
+ "ibc",
+ "ing"
+ };
+
+ if(drivertype >= BDRV_RBCH && drivertype <= BDRV_ING)
+ return(bdtab[drivertype]);
+ else
+ return("unknown");
+}
+
+/*---------------------------------------------------------------------------*
+ * process AOCD charging messages
+ *---------------------------------------------------------------------------*/
+void
+handle_charge(cfg_entry_t *cep)
+{
+ time_t now = time(NULL);
+
+ if(cep->aoc_last == 0) /* no last timestamp yet ? */
+ {
+ cep->aoc_last = now; /* add time stamp */
+ }
+ else if(cep->aoc_now == 0) /* no current timestamp yet ? */
+ {
+ cep->aoc_now = now; /* current timestamp */
+ }
+ else
+ {
+ cep->aoc_last = cep->aoc_now;
+ cep->aoc_now = now;
+ cep->aoc_diff = cep->aoc_now - cep->aoc_last;
+ cep->aoc_valid = AOC_VALID;
+ }
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_charge(cep);
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ if(do_monitor && accepted)
+ monitor_evnt_charge(cep, cep->charge, 0);
+#endif
+
+ if(cep->aoc_valid == AOC_VALID)
+ {
+ if(cep->aoc_diff != cep->unitlength)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "handle_charge: AOCD unit length updated %d -> %d secs", cep->unitlength, cep->aoc_diff)));
+
+ cep->unitlength = cep->aoc_diff;
+
+ unitlen_chkupd(cep);
+ }
+ else
+ {
+#ifdef NOTDEF
+ DBGL(DL_MSG, (log(LL_DBG, "handle_charge: AOCD unit length still %d secs", cep->unitlength)));
+#endif
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * update kernel idle_time, earlyhup_time and unitlen_time
+ *---------------------------------------------------------------------------*/
+void
+unitlen_chkupd(cfg_entry_t *cep)
+{
+ msg_timeout_upd_t tupd;
+
+ tupd.cdid = cep->cdid;
+
+ /* init the short hold data based on the shorthold algorithm type */
+
+ switch(cep->shorthold_algorithm)
+ {
+ case SHA_FIXU:
+ tupd.shorthold_data.shorthold_algorithm = SHA_FIXU;
+ tupd.shorthold_data.unitlen_time = cep->unitlength;
+ tupd.shorthold_data.idle_time = cep->idle_time_out;
+ tupd.shorthold_data.earlyhup_time = cep->earlyhangup;
+ break;
+
+ case SHA_VARU:
+ tupd.shorthold_data.shorthold_algorithm = SHA_VARU;
+ tupd.shorthold_data.unitlen_time = cep->unitlength;
+ tupd.shorthold_data.idle_time = cep->idle_time_out;
+ tupd.shorthold_data.earlyhup_time = 0;
+ break;
+ default:
+ log(LL_ERR, "unitlen_chkupd bad shorthold_algorithm %d", cep->shorthold_algorithm );
+ return;
+ break;
+ }
+
+ if((ioctl(isdnfd, I4B_TIMEOUT_UPD, &tupd)) < 0)
+ {
+ log(LL_ERR, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
+ error_exit(1, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * this is intended to be called by do_exit and closes down all
+ * active connections before the daemon exits or is reconfigured.
+ *--------------------------------------------------------------------------*/
+void
+close_allactive(void)
+{
+ int i, j, k;
+ cfg_entry_t *cep = NULL;
+
+ j = 0;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if((get_controller_state(i)) != CTRL_UP)
+ continue;
+
+ for (k = 0; k < isdn_ctrl_tab[i].nbch; k++)
+ {
+ if((ret_channel_state(i, k)) == CHAN_RUN)
+ {
+ if((cep = get_cep_by_cc(i, k)) != NULL)
+ {
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_disconnect(cep);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_evnt_disconnect(cep);
+#endif
+ next_state(cep, EV_DRQ);
+ j++;
+ }
+ }
+ }
+ }
+
+ if(j)
+ {
+ log(LL_DMN, "close_allactive: waiting for all connections terminated");
+ sleep(5);
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * set an interface up
+ *--------------------------------------------------------------------------*/
+void
+if_up(cfg_entry_t *cep)
+{
+ msg_updown_ind_t mui;
+
+ /* set interface up */
+
+ DBGL(DL_MSG, (log(LL_DBG, "if_up: taking %s%d up", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
+
+ mui.driver = cep->usrdevicename;
+ mui.driver_unit = cep->usrdeviceunit;
+ mui.updown = SOFT_ENA;
+
+ if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
+ {
+ log(LL_ERR, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ error_exit(1, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ }
+ cep->down_retry_count = 0;
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_updown(cep, 1);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_evnt_updown(cep, 1);
+#endif
+
+}
+
+/*--------------------------------------------------------------------------*
+ * set an interface down
+ *--------------------------------------------------------------------------*/
+void
+if_down(cfg_entry_t *cep)
+{
+ msg_updown_ind_t mui;
+
+ /* set interface up */
+
+ DBGL(DL_MSG, (log(LL_DBG, "if_down: taking %s%d down", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
+
+ mui.driver = cep->usrdevicename;
+ mui.driver_unit = cep->usrdeviceunit;
+ mui.updown = SOFT_DIS;
+
+ if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
+ {
+ log(LL_ERR, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ error_exit(1, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
+ }
+ cep->went_down_time = time(NULL);
+ cep->down_retry_count = 0;
+
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_updown(cep, 0);
+#endif
+#ifdef I4B_EXTERNAL_MONITOR
+ monitor_evnt_updown(cep, 0);
+#endif
+
+}
+
+/*--------------------------------------------------------------------------*
+ * send a dial response to (an interface in) the kernel
+ *--------------------------------------------------------------------------*/
+void
+dialresponse(cfg_entry_t *cep, int dstat)
+{
+ msg_dialout_resp_t mdr;
+
+ static char *stattab[] = {
+ "normal condition",
+ "temporary failure",
+ "permanent failure",
+ "dialout not allowed"
+ };
+
+ if(dstat < DSTAT_NONE || dstat > DSTAT_INONLY)
+ {
+ log(LL_ERR, "dialresponse: dstat out of range %d!", dstat);
+ return;
+ }
+
+ mdr.driver = cep->usrdevicename;
+ mdr.driver_unit = cep->usrdeviceunit;
+ mdr.stat = dstat;
+ mdr.cause = cep->disc_cause;
+
+ if((ioctl(isdnfd, I4B_DIALOUT_RESP, &mdr)) < 0)
+ {
+ log(LL_ERR, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
+ error_exit(1, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
+ }
+
+ DBGL(DL_DRVR, (log(LL_DBG, "dialresponse: sent [%s]", stattab[dstat])));
+}
+
+/*--------------------------------------------------------------------------*
+ * screening/presentation indicator
+ *--------------------------------------------------------------------------*/
+void
+handle_scrprs(int cdid, int scr, int prs, char *caller)
+{
+ /* screening indicator */
+
+ if(scr < SCR_NONE || scr > SCR_NET)
+ {
+ log(LL_ERR, "msg_connect_ind: invalid screening indicator value %d!", scr);
+ }
+ else
+ {
+ static char *scrtab[] = {
+ "no screening indicator",
+ "sreening user provided, not screened",
+ "screening user provided, verified & passed",
+ "screening user provided, verified & failed",
+ "screening network provided", };
+
+ if(extcallattr)
+ {
+ log(LL_CHD, "%05d %s %s", cdid, caller, scrtab[scr]);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "%s - %s", caller, scrtab[scr])));
+ }
+ }
+
+ /* presentation indicator */
+
+ if(prs < PRS_NONE || prs > PRS_RESERVED)
+ {
+ log(LL_ERR, "msg_connect_ind: invalid presentation indicator value %d!", prs);
+ }
+ else
+ {
+ static char *prstab[] = {
+ "no presentation indicator",
+ "presentation allowed",
+ "presentation restricted",
+ "number not available due to interworking",
+ "reserved presentation value" };
+
+ if(extcallattr)
+ {
+ log(LL_CHD, "%05d %s %s", cdid, caller, prstab[prs]);
+ }
+ else
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "%s - %s", caller, prstab[prs])));
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * check if the time is valid for an entry
+ *--------------------------------------------------------------------------*/
+static int
+isvalidtime(cfg_entry_t *cep)
+{
+ time_t t;
+ struct tm *tp;
+
+ if(cep->day == 0)
+ return(1);
+
+ t = time(NULL);
+ tp = localtime(&t);
+
+ if(cep->day & HD)
+ {
+ if(isholiday(tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900))
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: holiday %d.%d.%d", tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900)));
+ goto dayok;
+ }
+ }
+
+ if(cep->day & (1 << tp->tm_wday))
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: day match")));
+ goto dayok;
+ }
+
+ return(0);
+
+dayok:
+ if(cep->fromhr==0 && cep->frommin==0 && cep->tohr==0 && cep->tomin==0)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: no time specified, match!")));
+ return(1);
+ }
+
+ if(cep->tohr < cep->fromhr)
+ {
+ /* before 00:00 */
+
+ if( (tp->tm_hour > cep->fromhr) ||
+ (tp->tm_hour == cep->fromhr && tp->tm_min > cep->frommin) )
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t<f-1, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(1);
+ }
+
+ /* after 00:00 */
+
+ if( (tp->tm_hour < cep->tohr) ||
+ (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t<f-2, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(1);
+ }
+ }
+ else if(cep->fromhr == cep->tohr)
+ {
+ if(tp->tm_min >= cep->frommin && tp->tm_min < cep->tomin)
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: f=t, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(1);
+ }
+ }
+ else
+ {
+ if((tp->tm_hour > cep->fromhr && tp->tm_hour < cep->tohr) ||
+ (tp->tm_hour == cep->fromhr && tp->tm_min >= cep->frommin) ||
+ (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
+ {
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t>f, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+ return(1);
+ }
+ }
+ DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, no match!",
+ cep->fromhr, cep->frommin,
+ cep->tohr, cep->tomin,
+ tp->tm_hour, tp->tm_min)));
+
+ return(0);
+}
+
+/*--------------------------------------------------------------------------*
+ * prepend national or international prefix to a number
+ *--------------------------------------------------------------------------*/
+int add_number_prefix(char *number, int type_of_number)
+{
+ char tmp[TELNO_MAX];
+ char *prefix;
+ int result = 0;
+
+ if (type_of_number == TON_NATIONAL || type_of_number == TON_INTERNAT)
+ {
+ if (type_of_number == TON_NATIONAL)
+ prefix = prefixnational;
+ else
+ prefix = prefixinternational;
+
+ /* Add prefix only if not already there */
+ if (strncmp(number, prefix, strlen(prefix)) != 0)
+ {
+ snprintf(tmp, sizeof(tmp)-1, "%s%s", prefix, number);
+ strncpy(number, tmp, TELNO_MAX-1);
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/timer.c b/usr.sbin/i4b/isdnd/timer.c
new file mode 100644
index 0000000..34c0c43
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/timer.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - timer/timing support routines
+ * ------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:38:24 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+static int hr_callgate(void);
+static void handle_reserved(cfg_entry_t *cep, time_t now);
+static void handle_active(cfg_entry_t *cep, time_t now);
+static void recover_illegal(cfg_entry_t *cep);
+
+/*---------------------------------------------------------------------------*
+ * recover from illegal state
+ *---------------------------------------------------------------------------*/
+static void
+recover_illegal(cfg_entry_t *cep)
+{
+ log(LL_ERR, "recover_illegal: ERROR, entry %s attempting disconnect!", cep->name);
+ sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+ log(LL_ERR, "recover_illegal: ERROR, entry %s - reset state/cdid!", cep->name);
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+}
+
+/*---------------------------------------------------------------------------*
+ * start the timer
+ *---------------------------------------------------------------------------*/
+void
+start_timer(cfg_entry_t *cep, int seconds)
+{
+ cep->timerval = cep->timerremain = seconds;
+}
+
+/*---------------------------------------------------------------------------*
+ * stop the timer
+ *---------------------------------------------------------------------------*/
+void
+stop_timer(cfg_entry_t *cep)
+{
+ cep->timerval = cep->timerremain = 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * callgate for handle_recovery()
+ *---------------------------------------------------------------------------*/
+static int
+hr_callgate(void)
+{
+ static int tv_first = 1;
+ static struct timeval tv_last;
+ struct timeval tv_now;
+
+ /* there must be 1 sec minimum between calls to this section */
+
+ if(tv_first)
+ {
+ gettimeofday(&tv_last, NULL);
+ tv_first = 0;
+ }
+
+ gettimeofday(&tv_now, NULL);
+
+ if((tv_now.tv_sec - tv_last.tv_sec) < 1)
+ {
+
+ DBGL(DL_TIME, (log(LL_DBG, "time < 1 - last %ld:%ld now %ld:%ld",
+ tv_last.tv_sec, tv_last.tv_usec,
+ tv_now.tv_sec, tv_now.tv_usec)));
+ return(1);
+ }
+ else if((tv_now.tv_sec - tv_last.tv_sec) == 1)
+ {
+ if(((1000000 - tv_last.tv_usec) + tv_now.tv_usec) < 900000)
+ {
+ DBGL(DL_TIME, (log(LL_DBG, "time < 900000us - last %ld:%ld now %ld:%ld",
+ tv_last.tv_sec, tv_last.tv_usec,
+ tv_now.tv_sec, tv_now.tv_usec)));
+ return(1);
+ }
+ }
+
+ DBGL(DL_TIME, (log(LL_DBG, "time OK! - last %ld:%ld now %ld:%ld",
+ tv_last.tv_sec, tv_last.tv_usec,
+ tv_now.tv_sec, tv_now.tv_usec)));
+
+ gettimeofday(&tv_last, NULL);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout, recovery and retry handling
+ *---------------------------------------------------------------------------*/
+void
+handle_recovery(void)
+{
+ cfg_entry_t *cep = NULL;
+ int i;
+ time_t now;
+
+ if(hr_callgate()) /* last call to handle_recovery < 1 sec ? */
+ return; /* yes, exit */
+
+ now = time(NULL); /* get current time */
+
+ /* walk thru all entries, look for work to do */
+
+ for(i=0; i < nentries; i++)
+ {
+ cep = &cfg_entry_tab[i]; /* ptr to config entry */
+
+ if(cep->budget_callbackperiod && cep->budget_callbackncalls)
+ {
+ if(cep->budget_callbackperiod_time <= now)
+ {
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cback-budget-period (%d s, %d left)",
+ cep->name, cep->budget_callbackperiod, cep->budget_callbackncalls_cnt)));
+ cep->budget_callbackperiod_time = now + cep->budget_callbackperiod;
+ cep->budget_callbackncalls_cnt = cep->budget_callbackncalls;
+ }
+ }
+
+ if(cep->budget_calloutperiod && cep->budget_calloutncalls)
+ {
+ if(cep->budget_calloutperiod_time <= now)
+ {
+ DBGL(DL_BDGT, (log(LL_DBG, "%s: new cout-budget-period (%d s, %d left)",
+ cep->name, cep->budget_calloutperiod, cep->budget_calloutncalls_cnt)));
+ cep->budget_calloutperiod_time = now + cep->budget_calloutperiod;
+ cep->budget_calloutncalls_cnt = cep->budget_calloutncalls;
+ }
+ }
+
+ switch(cep->cdid)
+ {
+ case CDID_UNUSED: /* entry unused */
+ continue;
+ break;
+
+ case CDID_RESERVED: /* entry reserved */
+ handle_reserved(cep, now);
+ break;
+
+ default: /* entry in use */
+ handle_active(cep, now);
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout, recovery and retry handling for active entry
+ *---------------------------------------------------------------------------*/
+static void
+handle_active(cfg_entry_t *cep, time_t now)
+{
+ switch(cep->state)
+ {
+ case ST_ACCEPTED:
+ if(cep->timerval && (--(cep->timerremain)) <= 0)
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_active: entry %s, TIMEOUT !!!", cep->name)));
+ cep->timerval = cep->timerremain = 0;
+ next_state(cep, EV_TIMO);
+ }
+ break;
+
+ case ST_ALERT:
+ if(cep->alert_time > 0)
+ {
+ cep->alert_time--;
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s answering: incoming call from %s to %s",
+ cep->cdid, cep->name,
+ cep->real_phone_incoming.number,
+ cep->local_phone_incoming.number);
+ next_state(cep, EV_MCI);
+ }
+ break;
+
+ case ST_ILL:
+ recover_illegal(cep);
+ break;
+
+ default:
+ /* check hangup flag: if active, close connection */
+
+ if(cep->hangup)
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_active: entry %s, hangup request!", cep->name)));
+ cep->hangup = 0;
+ next_state(cep, EV_DRQ);
+ }
+
+ /* check maximum connect time reached */
+
+ if(cep->maxconnecttime > 0 && cep->connect_time > 0)
+ {
+ int connecttime = (int)difftime(now, cep->connect_time);
+ if(connecttime > cep->maxconnecttime)
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG,
+ "handle_active: entry %s, maxconnecttime %d reached!",
+ cep->name, cep->maxconnecttime)));
+ next_state(cep, EV_DRQ);
+ }
+ }
+
+ /*
+ * if shorthold mode is rates based, check if
+ * we entered a time with a new unit length
+ */
+
+ if(cep->unitlengthsrc == ULSRC_RATE)
+ {
+ int connecttime = (int)difftime(now, cep->connect_time);
+
+ if((connecttime > 1) &&
+ (connecttime % 60))
+ {
+ int newrate = get_current_rate(cep, 0);
+
+ if(newrate != cep->unitlength)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "handle_active: rates unit length updated %d -> %d", cep->unitlength, newrate)));
+
+ cep->unitlength = newrate;
+
+ unitlen_chkupd(cep);
+ }
+ }
+ }
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * timeout, recovery and retry handling for reserved entry
+ *---------------------------------------------------------------------------*/
+static void
+handle_reserved(cfg_entry_t *cep, time_t now)
+{
+ time_t waittime;
+
+ switch(cep->state)
+ {
+ case ST_DIALRTMRCHD: /* wait for dial retry time reached */
+
+ if(cep->dialrandincr)
+ waittime = cep->randomtime;
+ else
+ waittime = cep->recoverytime;
+
+
+ if(now > (cep->last_dial_time + waittime))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, dial retry request!", cep->name)));
+ cep->state = ST_DIALRETRY;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: dialretry get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: dialretry setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ break;
+
+
+ case ST_ACB_WAITDIAL: /* active callback wait for time between disconnect and dial */
+
+ if(now > (cep->last_release_time + cep->callbackwait))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, callback dial!", cep->name)));
+ cep->state = ST_ACB_DIAL;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: callback get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+
+ select_first_dialno(cep);
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: callback setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ break;
+
+ case ST_ACB_DIALFAIL: /* callback to remote failed */
+
+ if(cep->dialrandincr)
+ waittime = cep->randomtime + cep->recoverytime;
+ else
+ waittime = cep->recoverytime;
+
+ if(now > (cep->last_release_time + waittime))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, callback dial retry request!", cep->name)));
+ cep->state = ST_ACB_DIAL;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: callback dialretry get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: callback dialretry setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ break;
+
+ case ST_PCB_WAITCALL: /* wait for remote calling back */
+
+ if(now > (cep->last_release_time + cep->calledbackwait))
+ {
+ cep->dial_count++;
+
+ if(cep->dial_count < cep->dialretries)
+ {
+ /* inside normal retry cycle */
+
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, retry calledback dial #%d!",
+ cep->name, cep->dial_count)));
+ cep->state = ST_PCB_DIAL;
+
+ if((cep->cdid = get_cdid()) == 0)
+ {
+ log(LL_ERR, "handle_reserved: calledback get_cdid() returned 0!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ select_next_dialno(cep);
+
+ if((setup_dialout(cep)) == GOOD)
+ {
+ sendm_connect_req(cep);
+ }
+ else
+ {
+ log(LL_ERR, "handle_reserved: calledback setup_dialout returned ERROR!");
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ return;
+ }
+ }
+ else
+ {
+ /* retries exhausted */
+
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: calledback dial retries exhausted")));
+ dialresponse(cep, DSTAT_TFAIL);
+ cep->cdid = CDID_UNUSED;
+ cep->dial_count = 0;
+ cep->state = ST_IDLE;
+ }
+ }
+ break;
+
+ case ST_DOWN: /* interface was taken down */
+
+ if(now > (cep->went_down_time + cep->downtime))
+ {
+ DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: taking %s%d up", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
+ if_up(cep);
+ cep->state = ST_IDLE;
+ cep->cdid = CDID_UNUSED;
+ }
+ break;
+
+ case ST_ILL: /* illegal state reached, recover ! */
+
+ recover_illegal(cep);
+ break;
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndebug/Makefile b/usr.sbin/i4b/isdndebug/Makefile
new file mode 100644
index 0000000..5a31de9
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= isdndebug
+MAN= isdndebug.8
+SRCS= main.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdndebug/isdndebug.8 b/usr.sbin/i4b/isdndebug/isdndebug.8
new file mode 100644
index 0000000..6a49367
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/isdndebug.8
@@ -0,0 +1,109 @@
+.\"
+.\" Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdndebug.8,v 1.11 2000/05/31 08:15:29 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Wed May 31 10:15:07 2000]
+.\"
+.Dd May 31, 2000
+.Dt ISDNDEBUG 8
+.Os
+.Sh NAME
+.Nm isdndebug
+.Nd display and control isdn4bsd kernel variables and statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl e
+.Op Fl g
+.Op Fl l Ar layer
+.Op Fl m
+.Op Fl q
+.Op Fl r
+.Op Fl s Ar value
+.Op Fl u Ar unit
+.Op Fl z
+.Op Fl C
+.Op Fl Q
+.Sh DESCRIPTION
+The
+.Nm
+utility is part of the isdn4bsd package and is used to control the level of
+debugging output of the isdn4bsd kernel part.
+Every layer of the isdn4bsd kernel uses a debugging mask which can be
+manipulated using this utility.
+.Pp
+A second usage of
+.Nm
+is to display and reset chipset specific statistics and/or error counters
+and to display and reset the D-channel layer 2
+(Q.921 LAPD protocol) statistics and error counters.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Display chipset specific statistics and/or error counters.
+.It Fl e
+Set debugging mask for the selected layer(s) to display errors only.
+.It Fl g
+Get the debugging mask for the selected layer(s).
+.It Fl l
+Specify the layer for which a command applies. Default is all layers.
+.It Fl m
+Set debugging mask for the selected layer(s) to display all possible
+debugging messages (maximum output).
+.It Fl q
+Display the Q.921 (D-channel layer 2) frame receive/transmit statistics.
+.It Fl r
+Set debugging mask for the selected layer(s) to the compiled in default
+(reset).
+.It Fl s
+Set debugging mask for the selected layer(s) to value. Value can be
+specified in any number base supported by
+.Xr sscanf 3 .
+.It Fl u
+Set the unit numbers for the -c, -q, -C and -Q flags.
+.It Fl z
+Set debugging mask for the selected layer(s) to no output at all (zero).
+.It Fl H
+Reset the chipset specific statistics and/or error counters to zero.
+.It Fl Q
+Reset the Q.921 (D-channel layer 2) frame receive/transmit statistics to zero.
+.El
+.Sh FILES
+/dev/i4bctl
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdndebug -g
+.Ed
+.Pp
+displays the current debugging level for all ISDN layers
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdndebug/main.c b/usr.sbin/i4b/isdndebug/main.c
new file mode 100644
index 0000000..0620b3b
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/main.c
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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.c - i4b set debug options
+ * ------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon May 21 10:09:23 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <machine/i4b_debug.h>
+#include <machine/i4b_ioctl.h>
+
+char *bin_str(unsigned long val, int length);
+
+static void usage ( void );
+void printl1(unsigned long val);
+void printl2(unsigned long val);
+void printl3(unsigned long val);
+void printl4(unsigned long val);
+
+static int isdnfd;
+
+#define I4BCTLDEVICE "/dev/i4bctl"
+
+int opt_get = 0;
+int opt_layer = -1;
+int opt_set = 0;
+int opt_setval;
+int opt_reset = 0;
+int opt_max = 0;
+int opt_err = 0;
+int opt_zero = 0;
+int opt_unit = 0;
+int opt_lapd = 0;
+int opt_rlapd = 0;
+int opt_chipstat = 0;
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdndebug - i4b set debug level, version %d.%d.%d, compiled %s %s\n", VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdndebug -c -e -g -l <layer> -m -q -r -s <value> -u <unit> -z -C -Q\n");
+ fprintf(stderr, " -c get chipset statistics\n");
+ fprintf(stderr, " -e set error only debugging output\n");
+ fprintf(stderr, " -g get current debugging values\n");
+ fprintf(stderr, " -l layer specify layer (1...4)\n");
+ fprintf(stderr, " -m set maximum debugging output\n");
+ fprintf(stderr, " -q get Q.921 statistics\n");
+ fprintf(stderr, " -r reset values(s) to compiled in default\n");
+ fprintf(stderr, " -s value set new debugging value for layer\n");
+ fprintf(stderr, " -u unit unit number for -c, -q, -C and -Q commands\n");
+ fprintf(stderr, " -z set zero (=no) debugging output\n");
+ fprintf(stderr, " -C reset chipset statistics\n");
+ fprintf(stderr, " -Q reset Q.921 statistics\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ ctl_debug_t cdbg;
+ int ret;
+
+ while ((c = getopt(argc, argv, "ceghl:mqrs:u:zCHQ")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ opt_chipstat = 1;
+ break;
+
+ case 'e':
+ opt_err = 1;
+ break;
+
+ case 'g':
+ opt_get = 1;
+ break;
+
+ case 'q':
+ opt_lapd = 1;
+ break;
+
+ case 'r':
+ opt_reset = 1;
+ break;
+
+ case 'm':
+ opt_max = 1;
+ break;
+
+ case 'l':
+ opt_layer = atoi(optarg);
+ if(opt_layer < 1 || opt_layer > 4)
+ usage();
+ break;
+
+ case 's':
+ if((sscanf(optarg, "%i", &opt_setval)) != 1)
+ usage();
+ opt_set = 1;
+ break;
+
+ case 'u':
+ opt_unit = atoi(optarg);
+ if(opt_unit < 0 || opt_unit > 9)
+ usage();
+ break;
+
+ case 'z':
+ opt_zero = 1;
+ break;
+
+ case 'Q':
+ opt_rlapd = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(opt_get == 0 && opt_set == 0 && opt_reset == 0 && opt_max == 0 &&
+ opt_err == 0 && opt_zero == 0 && opt_lapd == 0 && opt_rlapd == 0 &&
+ opt_chipstat == 0)
+ {
+ usage();
+ }
+
+ if((opt_get + opt_set + opt_reset + opt_max + opt_err + opt_zero +
+ opt_lapd + opt_rlapd + opt_chipstat) > 1)
+ {
+ usage();
+ }
+
+ if((isdnfd = open(I4BCTLDEVICE, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "i4bctl: cannot open %s: %s\n", I4BCTLDEVICE, strerror(errno));
+ exit(1);
+ }
+
+ if(opt_chipstat)
+ {
+ struct chipstat cst;
+ u_char *name;
+
+ cst.driver_unit = opt_unit;
+ cst.driver_bchannel = 0;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_CHIPSTAT, &cst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_CHIPSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ switch(cst.driver_type)
+ {
+ case L1DRVR_ISIC:
+ name = "isic";
+ printf("\nisic-driver\nHSCX events: VFR RDO CRC RAB XDU RFO\n");
+
+ printf("unit %d chan %d: %6d %6d %6d %6d %6d %6d\n",
+ cst.stats.hscxstat.unit,
+ cst.stats.hscxstat.chan,
+ cst.stats.hscxstat.vfr,
+ cst.stats.hscxstat.rdo,
+ cst.stats.hscxstat.crc,
+ cst.stats.hscxstat.rab,
+ cst.stats.hscxstat.xdu,
+ cst.stats.hscxstat.rfo);
+
+ cst.driver_unit = opt_unit;
+ cst.driver_bchannel = 1;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_CHIPSTAT, &cst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_CHIPSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("HSCX events: VFR RDO CRC RAB XDU RFO\n");
+
+ printf("unit %d chan %d: %6d %6d %6d %6d %6d %6d\n",
+ cst.stats.hscxstat.unit,
+ cst.stats.hscxstat.chan,
+ cst.stats.hscxstat.vfr,
+ cst.stats.hscxstat.rdo,
+ cst.stats.hscxstat.crc,
+ cst.stats.hscxstat.rab,
+ cst.stats.hscxstat.xdu,
+ cst.stats.hscxstat.rfo);
+
+ break;
+
+ case L1DRVR_IWIC:
+ name = "iwic";
+ break;
+
+ case L1DRVR_IFPI:
+ name = "ifpi";
+ break;
+
+ case L1DRVR_IHFC:
+ name = "ihfc";
+ break;
+
+ case L1DRVR_IFPNP:
+ name = "ifpnp";
+ break;
+
+ default:
+ fprintf(stderr, "ioctl I4B_CTL_GET_CHIPSTAT, unknown driver %d\n",cst.driver_type);
+ exit(1);
+ break;
+ }
+ exit(0);
+ }
+
+ if(opt_lapd)
+ {
+ l2stat_t l2s;
+
+ l2s.unit = opt_unit;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_LAPDSTAT, &l2s)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_LAPDSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("unit %d Q.921 statistics: receive transmit\n", opt_unit);
+ printf("---------------------------------------------\n");
+ printf("# of I-frames %12lu %12lu\n", l2s.lapdstat.rx_i, l2s.lapdstat.tx_i);
+ printf("# of RR-frames %12lu %12lu\n", l2s.lapdstat.rx_rr, l2s.lapdstat.tx_rr);
+ printf("# of RNR-frames %12lu %12lu\n", l2s.lapdstat.rx_rnr, l2s.lapdstat.tx_rnr);
+ printf("# of REJ-frames %12lu %12lu\n", l2s.lapdstat.rx_rej, l2s.lapdstat.tx_rej);
+ printf("# of SABME-frames %12lu %12lu\n", l2s.lapdstat.rx_sabme, l2s.lapdstat.tx_sabme);
+ printf("# of DM-frames %12lu %12lu\n", l2s.lapdstat.rx_dm, l2s.lapdstat.tx_dm);
+ printf("# of DISC-frames %12lu %12lu\n", l2s.lapdstat.rx_disc, l2s.lapdstat.tx_disc);
+ printf("# of UA-frames %12lu %12lu\n", l2s.lapdstat.rx_ua, l2s.lapdstat.tx_ua);
+ printf("# of FRMR-frames %12lu %12lu\n", l2s.lapdstat.rx_frmr, l2s.lapdstat.tx_frmr);
+ printf("# of TEI-frames %12lu %12lu\n", l2s.lapdstat.rx_tei, l2s.lapdstat.tx_tei);
+ printf("# of UI-frames %12lu \n", l2s.lapdstat.rx_ui);
+ printf("# of XID-frames %12lu \n", l2s.lapdstat.rx_xid);
+ printf(" errors\n");
+ printf("---------------------------------------------\n");
+ printf("# of frames with incorrect length%12lu\n", l2s.lapdstat.err_rx_len);
+ printf("# of frames with bad frame type %12lu\n", l2s.lapdstat.err_rx_badf);
+ printf("# of bad S frames %12lu\n", l2s.lapdstat.err_rx_bads);
+ printf("# of bad U frames %12lu\n", l2s.lapdstat.err_rx_badu);
+ printf("# of bad UI frames %12lu\n", l2s.lapdstat.err_rx_badui);
+
+ exit(0);
+ }
+
+ if(opt_rlapd)
+ {
+ int unit;
+
+ unit = opt_unit;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_CLR_LAPDSTAT, &unit)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_CLR_LAPDSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("Q.921 statistics counters unit %d reset to zero!\n", unit);
+ exit(0);
+ }
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_DEBUG, &cdbg)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_DEBUG failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ if(opt_get)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ printl1(cdbg.l1);
+ printl2(cdbg.l2);
+ printl3(cdbg.l3);
+ printl4(cdbg.l4);
+ break;
+
+ case 1:
+ printl1(cdbg.l1);
+ break;
+
+ case 2:
+ printl2(cdbg.l2);
+ break;
+
+ case 3:
+ printl3(cdbg.l3);
+ break;
+
+ case 4:
+ printl4(cdbg.l4);
+ break;
+ }
+ printf("\n");
+ return(0);
+ }
+ else if(opt_set)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ usage();
+ break;
+
+ case 1:
+ cdbg.l1 = opt_setval;
+ break;
+
+ case 2:
+ cdbg.l2 = opt_setval;
+ break;
+
+ case 3:
+ cdbg.l3 = opt_setval;
+ break;
+
+ case 4:
+ cdbg.l4 = opt_setval;
+ break;
+ }
+ }
+ else if(opt_reset)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = L1_DEBUG_DEFAULT;
+ cdbg.l2 = L2_DEBUG_DEFAULT;
+ cdbg.l3 = L3_DEBUG_DEFAULT;
+ cdbg.l4 = L4_DEBUG_DEFAULT;
+ break;
+
+ case 1:
+ cdbg.l1 = L1_DEBUG_DEFAULT;
+ break;
+
+ case 2:
+ cdbg.l2 = L2_DEBUG_DEFAULT;
+ break;
+
+ case 3:
+ cdbg.l3 = L3_DEBUG_DEFAULT;
+ break;
+
+ case 4:
+ cdbg.l4 = L4_DEBUG_DEFAULT;
+ break;
+ }
+ }
+ else if(opt_max)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = L1_DEBUG_MAX;
+ cdbg.l2 = L2_DEBUG_MAX;
+ cdbg.l3 = L3_DEBUG_MAX;
+ cdbg.l4 = L4_DEBUG_MAX;
+ break;
+
+ case 1:
+ cdbg.l1 = L1_DEBUG_MAX;
+ break;
+
+ case 2:
+ cdbg.l2 = L2_DEBUG_MAX;
+ break;
+
+ case 3:
+ cdbg.l3 = L3_DEBUG_MAX;
+ break;
+
+ case 4:
+ cdbg.l4 = L4_DEBUG_MAX;
+ break;
+ }
+ }
+ else if(opt_err)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = L1_DEBUG_ERR;
+ cdbg.l2 = L2_DEBUG_ERR;
+ cdbg.l3 = L3_DEBUG_ERR;
+ cdbg.l4 = L4_DEBUG_ERR;
+ break;
+
+ case 1:
+ cdbg.l1 = L1_DEBUG_ERR;
+ break;
+
+ case 2:
+ cdbg.l2 = L2_DEBUG_ERR;
+ break;
+
+ case 3:
+ cdbg.l3 = L3_DEBUG_ERR;
+ break;
+
+ case 4:
+ cdbg.l4 = L4_DEBUG_ERR;
+ break;
+ }
+ }
+ else if(opt_zero)
+ {
+ switch(opt_layer)
+ {
+ case -1:
+ cdbg.l1 = 0;
+ cdbg.l2 = 0;
+ cdbg.l3 = 0;
+ cdbg.l4 = 0;
+ break;
+
+ case 1:
+ cdbg.l1 = 0;
+ break;
+
+ case 2:
+ cdbg.l2 = 0;
+ break;
+
+ case 3:
+ cdbg.l3 = 0;
+ break;
+
+ case 4:
+ cdbg.l4 = 0;
+ break;
+ }
+ }
+ else
+ {
+ exit(1);
+ }
+
+ if((ret = ioctl(isdnfd, I4B_CTL_SET_DEBUG, &cdbg)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_SET_DEBUG failed: %s", strerror(errno));
+ exit(1);
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * return ptr to string of 1's and 0's for value
+ *---------------------------------------------------------------------------*/
+char *
+bin_str(unsigned long val, int length)
+{
+ static char buffer[80];
+ int i = 0;
+
+ if (length > 32)
+ length = 32;
+
+ val = val << (32 - length);
+
+ while (length--)
+ {
+ if (val & 0x80000000)
+ buffer[i++] = '1';
+ else
+ buffer[i++] = '0';
+ if ((length % 4) == 0 && length)
+ buffer[i++] = '.';
+ val = val << 1;
+ }
+ return (buffer);
+}
+
+/*---------------------------------------------------------------------------*
+ * print l1 info
+ *---------------------------------------------------------------------------*/
+void
+printl1(unsigned long val)
+{
+ printf("\nLayer 1: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" | |||| |||| |||| ||||\n"),
+ printf(" | |||| |||| |||| |||+- general error messages\n");
+ printf(" | |||| |||| |||| ||+-- PH primitives exchanged\n");
+ printf(" | |||| |||| |||| |+--- B channel actions\n");
+ printf(" | |||| |||| |||| +---- HSCX error messages\n");
+ printf(" | |||| |||| |||+------ HSCX IRQ messages\n");
+ printf(" | |||| |||| ||+------- ISAC error messages\n");
+ printf(" | |||| |||| |+-------- ISAC messages\n");
+ printf(" | |||| |||| +--------- ISAC setup messages\n");
+ printf(" | |||| |||+----------- FSM general messages\n");
+ printf(" | |||| ||+------------ FSM error messages\n");
+ printf(" | |||| |+------------- timer general messages\n");
+ printf(" | |||| +-------------- timer error messages\n");
+ printf(" | |||+---------------- HSCX data xfer errors msgs\n");
+ printf(" | ||+----------------- ISAC CICO messages\n");
+ printf(" | |+------------------ silent messages (soft-HDLC)\n");
+ printf(" | +------------------- error messages (soft-HDLC)\n");
+ printf(" +--------------------- HFC-S PCI debug messages\n");
+ printf(" ++++-++++-++++-+++---------------------- unassigned\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * print l2 info
+ *---------------------------------------------------------------------------*/
+void
+printl2(unsigned long val)
+{
+ printf("\nLayer 2: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" || |||| |||| ||||\n"),
+ printf(" || |||| |||| |||+- general error messages\n");
+ printf(" || |||| |||| ||+-- DL primitives exchanged\n");
+ printf(" || |||| |||| |+--- U frame messages\n");
+ printf(" || |||| |||| +---- U frame error messages\n");
+ printf(" || |||| |||+------ S frame messages\n");
+ printf(" || |||| ||+------- S frame error messages\n");
+ printf(" || |||| |+-------- I frame messages\n");
+ printf(" || |||| +--------- I frame error messages\n");
+ printf(" || |||+----------- FSM general messages\n");
+ printf(" || ||+------------ FSM error messages\n");
+ printf(" || |+------------- timer general messages\n");
+ printf(" || +-------------- timer error messages\n");
+ printf(" |+---------------- TEI general messages\n");
+ printf(" +----------------- TEI error messages\n");
+ printf(" ++++-++++-++++-++++-++------------------ unassigned\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * print l3 info
+ *---------------------------------------------------------------------------*/
+void
+printl3(unsigned long val)
+{
+ printf("\nLayer 3: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" ||| |||| ||||\n"),
+ printf(" ||| |||| |||+- general error messages\n");
+ printf(" ||| |||| ||+-- general messages\n");
+ printf(" ||| |||| |+--- FSM messages\n");
+ printf(" ||| |||| +---- FSM error messages\n");
+ printf(" ||| |||+------ timer messages\n");
+ printf(" ||| ||+------- timer error messages\n");
+ printf(" ||| |+-------- protocol messages\n");
+ printf(" ||| +--------- protocol error messages\n");
+ printf(" ||+----------- facility messages\n");
+ printf(" |+------------ facility error messages\n");
+ printf(" +------------- Q.931 messages exchanged\n");
+ printf(" ++++-++++-++++-++++-++++-+-------------- unassigned\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * print l4 info
+ *---------------------------------------------------------------------------*/
+void
+printl4(unsigned long val)
+{
+ printf("\nLayer 4: %s = 0x%lX\n", bin_str(val, 32), val);
+ printf(" ||| |||| ||||\n"),
+ printf(" ||| |||| |||+- general error messages\n");
+ printf(" ||| |||| ||+-- general messages\n");
+ printf(" ||| |||| |+--- B-ch timeout messages\n");
+ printf(" ||| |||| +---- network driver dial state\n");
+ printf(" ||| |||+------ ipr driver debug messages\n");
+ printf(" ||| ||+------- rbch driver debug messages\n");
+ printf(" ||| |+-------- isp driver debug messages\n");
+ printf(" ||| +--------- tel driver debug messages\n");
+ printf(" ||+----------- ing driver debug messages\n");
+ printf(" |+------------ iavc driver debug messages\n");
+ printf(" +------------- capi driver debug messages\n");
+ printf(" ++++-++++-++++-++++-++++---------------- unassigned\n");
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/Makefile b/usr.sbin/i4b/isdndecode/Makefile
new file mode 100644
index 0000000..9dbd971
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= isdndecode
+MAN= isdndecode.8
+SRCS= main.c layer1.c layer2.c layer3.c layer3_subr.c facility.c pcause.c
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/i4b/isdndecode/decode.h b/usr.sbin/i4b/isdndecode/decode.h
new file mode 100644
index 0000000..83bab00
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/decode.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * decode.h - isdndecode header file
+ * ---------------------------------
+ *
+ * $Id: decode.h,v 1.6 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:49:50 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_trace.h>
+
+#include "pcause.h"
+
+#define I4BTRC_DEVICE "/dev/i4btrc" /* trace device file */
+#define DECODE_FILE_NAME "isdndecode" /* default output filename */
+#define DECODE_FILE_NAME_BAK ".last" /* backup filename trailer */
+#define BIN_FILE_NAME "isdntracebin" /* default binary filename */
+
+#define BSIZE 4096 /* read buffer size */
+#define NCOLS 80 /* screen width */
+
+#define RxUDEF 0 /* analyze mode, default unit for receiver side */
+#define TxUDEF 1 /* analyze mode, default unit for transmitter side */
+
+void layer1(char *pbuf, unsigned char *buf);
+int layer2(char *pbuf, unsigned char *buf, int is_te, int printit);
+void layer3(char *pbuf, int n, int off, unsigned char *buf);
+int q932_facility(char *pbuf, unsigned char *buf);
+void sprintline(int, char *, int, int, int, const char *, ...);
+void extension(int, char *, int, unsigned char, unsigned char);
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/facility.c b/usr.sbin/i4b/isdndecode/facility.c
new file mode 100644
index 0000000..c4502a8
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/facility.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * facility.c - decode Q.932 facilities
+ * ------------------------------------
+ *
+ * $Id: facility.c,v 1.5 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 16:15:43 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+#include "facility.h"
+
+static int do_component(int length, char *pbuf);
+static char *uni_str(int code);
+static char *opval_str(int val);
+static char *bid_str(int val);
+static void next_state(char *pbuf, int class, int form, int code, int val);
+
+static int byte_len;
+static unsigned char *byte_buf;
+static int state;
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931/Q.932 facility info element
+ *---------------------------------------------------------------------------*/
+int
+q932_facility(char *pbuf, unsigned char *buf)
+{
+ int len;
+
+ sprintf((pbuf+strlen(pbuf)), "[facility (Q.932): ");
+
+ buf++; /* length */
+
+ len = *buf;
+
+ buf++; /* protocol profile */
+
+ sprintf((pbuf+strlen(pbuf)), "Protocol=");
+
+ switch(*buf & 0x1f)
+ {
+ case FAC_PROTO_ROP:
+ sprintf((pbuf+strlen(pbuf)), "Remote Operations Protocol\n");
+ break;
+
+ case FAC_PROTO_CMIP:
+ sprintf((pbuf+strlen(pbuf)), "CMIP Protocol (Q.941), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ case FAC_PROTO_ACSE:
+ sprintf((pbuf+strlen(pbuf)), "ACSE Protocol (X.217/X.227), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "Unknown Protocol (val = 0x%x), UNSUPPORTED!\n", *buf & 0x1f);
+ return(len+2);
+ break;
+ }
+
+ /* next byte */
+
+ buf++;
+ len--;
+
+ /* initialize variables for do_component */
+
+ byte_len = 0;
+ byte_buf = buf;
+ state = ST_EXP_COMP_TYP;
+
+ /* decode facility */
+
+ do_component(len, pbuf);
+
+ sprintf((pbuf+(strlen(pbuf)-1)), "]"); /* XXX replace last newline */
+
+ return(len+3);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle a component recursively
+ *---------------------------------------------------------------------------*/
+static int
+do_component(int length, char *pbuf)
+{
+ int comp_tag_class; /* component tag class */
+ int comp_tag_form; /* component form: constructor or primitive */
+ int comp_tag_code; /* component code depending on class */
+ int comp_length = 0; /* component length */
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "ENTER - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+again:
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "AGAIN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+ /*----------------------------------------*/
+ /* first component element: component tag */
+ /*----------------------------------------*/
+
+ /* tag class bits */
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Tag: ", *byte_buf);
+
+ comp_tag_class = (*byte_buf & 0xc0) >> 6;
+
+ switch(comp_tag_class)
+ {
+ case FAC_TAGCLASS_UNI:
+ sprintf((pbuf+strlen(pbuf)), "Universal");
+ break;
+ case FAC_TAGCLASS_APW:
+ sprintf((pbuf+strlen(pbuf)), "Applic-wide");
+ break;
+ case FAC_TAGCLASS_COS:
+ sprintf((pbuf+strlen(pbuf)), "Context-spec");
+ break;
+ case FAC_TAGCLASS_PRU:
+ sprintf((pbuf+strlen(pbuf)), "Private");
+ break;
+ }
+
+ /* tag form bit */
+
+ comp_tag_form = (*byte_buf & 0x20) > 5;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_form == FAC_TAGFORM_CON)
+ {
+ sprintf((pbuf+strlen(pbuf)), "Constructor");
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "Primitive");
+ }
+
+ /* tag code bits */
+
+ comp_tag_code = *byte_buf & 0x1f;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_code == 0x1f)
+ {
+ comp_tag_code = 0;
+
+ byte_buf++;
+ byte_len++;
+
+ while(*byte_buf & 0x80)
+ {
+ comp_tag_code += (*byte_buf & 0x7f);
+ byte_buf++;
+ byte_len++;
+ }
+ comp_tag_code += (*byte_buf & 0x7f);
+ sprintf((pbuf+strlen(pbuf)), "%d (ext)\n", comp_tag_code);
+ }
+ else
+ {
+ comp_tag_code = (*byte_buf & 0x1f);
+
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ sprintf((pbuf+strlen(pbuf)), "%s (%d)\n", uni_str(comp_tag_code), comp_tag_code);
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "code = %d\n", comp_tag_code);
+ }
+ }
+
+ byte_buf++;
+ byte_len++;
+
+ /*--------------------------------------------*/
+ /* second component element: component length */
+ /*--------------------------------------------*/
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Len: ", *byte_buf);
+
+ comp_length = 0;
+
+ if(*byte_buf & 0x80)
+ {
+ int i = *byte_buf & 0x7f;
+
+ byte_len += i;
+
+ for(;i > 0;i++)
+ {
+ byte_buf++;
+ comp_length += (*byte_buf * (i*256));
+ }
+ sprintf((pbuf+strlen(pbuf)), "%d (long form)\n", comp_length);
+ }
+ else
+ {
+ comp_length = *byte_buf & 0x7f;
+ sprintf((pbuf+strlen(pbuf)), "%d (short form)\n", comp_length);
+ }
+
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, -1);
+
+ byte_len++;
+ byte_buf++;
+
+ if(comp_length)
+ {
+
+ /*---------------------------------------------*/
+ /* third component element: component contents */
+ /*---------------------------------------------*/
+
+ if(comp_tag_form) /* == constructor */
+ {
+ do_component(comp_length, pbuf);
+ }
+ else
+ {
+ int val = 0;
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ switch(comp_tag_code)
+ {
+ case FAC_CODEUNI_INT:
+ case FAC_CODEUNI_ENUM:
+ case FAC_CODEUNI_BOOL:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x ", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "Val: %d\n", val);
+ }
+ break;
+ default:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x = %d", *byte_buf, *byte_buf);
+ if(isprint(*byte_buf))
+ sprintf((pbuf+strlen(pbuf)), " = '%c'", *byte_buf);
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ }
+ break;
+ }
+ }
+
+ else /* comp_tag_class != FAC_TAGCLASS_UNI */
+ {
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "\n");
+ }
+ }
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, val);
+ }
+ }
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "PREGOTO - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ if(byte_len < length)
+ goto again;
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "RETURN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ return(byte_len);
+}
+
+/*---------------------------------------------------------------------------*
+ * print universal id type
+ *---------------------------------------------------------------------------*/
+static char *uni_str(int code)
+{
+ static char *tbl[] = {
+ "BOOLEAN",
+ "INTEGER",
+ "BIT STRING",
+ "OCTET STRING",
+ "NULL",
+ "OBJECT IDENTIFIER",
+ "OBJECT DESCRIPTOR",
+ "EXTERNAL",
+ "REAL",
+ "ENUMERATED",
+ "RESERVED11",
+ "RESERVED12",
+ "RESERVED13",
+ "RESERVED14",
+ "RESERVED15",
+ "SEQUENCE",
+ "SET",
+ "NUMERIC STRING",
+ "PRINTABLE STRING",
+ "TELETEX STRING",
+ "ISO646 STRING",
+ "IA5 STRING",
+ "GRAPHIC STRING",
+ "GENERAL STRING"
+ };
+
+ if(code >= 1 && code <= FAC_CODEUNI_GNSTR)
+ return(tbl[code-1]);
+ else
+ return("ERROR, Value out of Range!");
+}
+
+/*---------------------------------------------------------------------------*
+ * print operation value
+ *---------------------------------------------------------------------------*/
+static char *opval_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case FAC_OPVAL_UUS:
+ r = "uUs";
+ break;
+ case FAC_OPVAL_CUG:
+ r = "cUGCall";
+ break;
+ case FAC_OPVAL_MCID:
+ r = "mCIDRequest";
+ break;
+ case FAC_OPVAL_BTPY:
+ r = "beginTPY";
+ break;
+ case FAC_OPVAL_ETPY:
+ r = "endTPY";
+ break;
+ case FAC_OPVAL_ECT:
+ r = "eCTRequest";
+ break;
+ case FAC_OPVAL_DIV_ACT:
+ r = "activationDiversion";
+ break;
+ case FAC_OPVAL_DIV_DEACT:
+ r = "deactivationDiversion";
+ break;
+ case FAC_OPVAL_DIV_ACTSN:
+ r = "activationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_DEACTSN:
+ r = "deactivationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_INTER:
+ r = "interrogationDiversion";
+ break;
+ case FAC_OPVAL_DIV_INFO:
+ r = "diversionInformation";
+ break;
+ case FAC_OPVAL_DIV_CALLDEF:
+ r = "callDeflection";
+ break;
+ case FAC_OPVAL_DIV_CALLRER:
+ r = "callRerouting";
+ break;
+ case FAC_OPVAL_DIV_LINF2:
+ r = "divertingLegInformation2";
+ break;
+ case FAC_OPVAL_DIV_INVS:
+ r = "invokeStatus";
+ break;
+ case FAC_OPVAL_DIV_INTER1:
+ r = "interrogationDiversion1";
+ break;
+ case FAC_OPVAL_DIV_LINF1:
+ r = "divertingLegInformation1";
+ break;
+ case FAC_OPVAL_DIV_LINF3:
+ r = "divertingLegInformation3";
+ break;
+ case FAC_OPVAL_ER_CRCO:
+ r = "explicitReservationCreationControl";
+ break;
+ case FAC_OPVAL_ER_MGMT:
+ r = "explicitReservationManagement";
+ break;
+ case FAC_OPVAL_ER_CANC:
+ r = "explicitReservationCancel";
+ break;
+ case FAC_OPVAL_MLPP_QUERY:
+ r = "mLPP lfb Query";
+ break;
+ case FAC_OPVAL_MLPP_CALLR:
+ r = "mLPP Call Request";
+ break;
+ case FAC_OPVAL_MLPP_CALLP:
+ r = "mLPP Call Preemption";
+ break;
+ case FAC_OPVAL_AOC_REQ:
+ r = "chargingRequest";
+ break;
+ case FAC_OPVAL_AOC_S_CUR:
+ r = "aOCSCurrency";
+ break;
+ case FAC_OPVAL_AOC_S_SPC:
+ r = "aOCSSpecialArrangement";
+ break;
+ case FAC_OPVAL_AOC_D_CUR:
+ r = "aOCDCurrency";
+ break;
+ case FAC_OPVAL_AOC_D_UNIT:
+ r = "aOCDChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_E_CUR:
+ r = "aOCECurrency";
+ break;
+ case FAC_OPVAL_AOC_E_UNIT:
+ r = "aOCEChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_IDOFCRG:
+ r = "identificationOfCharge";
+ break;
+ case FAC_OPVAL_CONF_BEG:
+ r = "beginConf";
+ break;
+ case FAC_OPVAL_CONF_ADD:
+ r = "addConf";
+ break;
+ case FAC_OPVAL_CONF_SPLIT:
+ r = "splitConf";
+ break;
+ case FAC_OPVAL_CONF_DROP:
+ r = "dropConf";
+ break;
+ case FAC_OPVAL_CONF_ISOLATE:
+ r = "isolateConf";
+ break;
+ case FAC_OPVAL_CONF_REATT:
+ r = "reattachConf";
+ break;
+ case FAC_OPVAL_CONF_PDISC:
+ r = "partyDISC";
+ break;
+ case FAC_OPVAL_CONF_FCONF:
+ r = "floatConf";
+ break;
+ case FAC_OPVAL_CONF_END:
+ r = "endConf";
+ break;
+ case FAC_OPVAL_CONF_IDCFE:
+ r = "indentifyConferee";
+ break;
+ case FAC_OPVAL_REVC_REQ:
+ r = "requestREV";
+ break;
+ default:
+ sprintf(buffer, "unknown operation value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * billing id string
+ *---------------------------------------------------------------------------*/
+static char *bid_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case 0:
+ r = "normalCharging";
+ break;
+ case 1:
+ r = "reverseCharging";
+ break;
+ case 2:
+ r = "creditCardCharging";
+ break;
+ case 3:
+ r = "callForwardingUnconditional";
+ break;
+ case 4:
+ r = "callForwardingBusy";
+ break;
+ case 5:
+ r = "callForwardingNoReply";
+ break;
+ case 6:
+ r = "callDeflection";
+ break;
+ case 7:
+ r = "callTransfer";
+ break;
+ default:
+ sprintf(buffer, "unknown billing-id value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component
+ *---------------------------------------------------------------------------*/
+static void
+F_1_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t invokeComponent\n");
+ state = ST_EXP_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result
+ *---------------------------------------------------------------------------*/
+static void
+F_1_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnResult\n");
+ state = ST_EXP_RR_INV_ID;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * return error
+ *---------------------------------------------------------------------------*/
+static void
+F_1_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_3, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnError\n");
+ state = ST_EXP_NIX;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * reject
+ *---------------------------------------------------------------------------*/
+static void
+F_1_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t reject\n");
+ state = ST_EXP_REJ_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_REJ_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, general problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ30(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ30, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t General problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized component\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped component\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = badly structured component\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, invoke problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ31(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ31, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Invoke problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = duplicate invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized operation\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped argument\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = resource limitation\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = initiator releasing\n");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized linked identifier\n");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = linked resonse unexpected\n");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected child operation\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return result problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ32(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ32, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return result problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = return response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped result\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return error problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ33(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ33, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return error problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = error response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized error\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected error\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped parameter\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RR2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_RR_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_INFO;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_RR3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_RR_RESULT;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: RESULT
+ *---------------------------------------------------------------------------*/
+static void
+F_RRR(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RRR, val = %d\n", val);
+#endif
+ state = ST_EXP_NIX;
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t specificChargingUnits\n");
+ state = ST_EXP_RUL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t freeOfCharge\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t chargeNotAvailable\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_5(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_5, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t recordedUnitsList [1]\n");
+ state = ST_EXP_RU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_6(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_6, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t RecordedUnits\n");
+ state = ST_EXP_RNOU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_7(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_7, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t NumberOfUnits = %d\n", val);
+ state = ST_EXP_TOCI;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_8(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_8, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t typeOfChargingInfo = %s\n", val == 0 ? "subTotal" : "total");
+ state = ST_EXP_DBID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_9(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_9, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t AOCDBillingId = %s (%d)\n", bid_str(val), val);
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * state table
+ *---------------------------------------------------------------------------*/
+static struct statetab {
+ int currstate; /* input: current state we are in */
+ int form; /* input: current tag form */
+ int class; /* input: current tag class */
+ int code; /* input: current tag code */
+ void (*func)(char *,int); /* output: func to exec */
+} statetab[] = {
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+
+/* invoke */
+
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_1_1 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 2, F_1_2 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 3, F_1_3 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 4, F_1_4 },
+ {ST_EXP_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_2 },
+ {ST_EXP_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_3 },
+ {ST_EXP_INFO, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_4 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_NULL, F_4_1 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_4_2 },
+ {ST_EXP_RUL, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_5 },
+ {ST_EXP_RU, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_6 },
+ {ST_EXP_RNOU, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_7 },
+ {ST_EXP_TOCI, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_8 },
+ {ST_EXP_DBID, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_9 },
+
+/* return result */
+
+ {ST_EXP_RR_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR2 },
+ {ST_EXP_RR_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR3 },
+ {ST_EXP_RR_RESULT, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SET, F_RRR },
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+/* reject */
+
+ {ST_EXP_REJ_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RJ2 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 0, F_RJ30 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_RJ31 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_RJ32 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_RJ33 },
+
+/* end */
+
+ {-1, -1, -1, -1, NULL }
+};
+
+/*---------------------------------------------------------------------------*
+ * state decode for do_component
+ *---------------------------------------------------------------------------*/
+static void
+next_state(char *pbuf, int class, int form, int code, int val)
+{
+ int i;
+
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: class=%d, form=%d, code=%d, val=%d\n", class, form, code, val);
+#endif
+
+ for(i=0; ; i++)
+ {
+ if((statetab[i].currstate > state) ||
+ (statetab[i].currstate == -1))
+ {
+ break;
+ }
+
+ if((statetab[i].currstate == state) &&
+ (statetab[i].form == form) &&
+ (statetab[i].class == class) &&
+ (statetab[i].code == code))
+ {
+ (*statetab[i].func)(pbuf, val);
+ break;
+ }
+ }
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/facility.h b/usr.sbin/i4b/isdndecode/facility.h
new file mode 100644
index 0000000..f4cfe10
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/facility.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * facility.h - facility header file
+ * ---------------------------------
+ *
+ * $Id: facility.h,v 1.5 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 16:16:04 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+/* #define FAC_DEBUG */
+/* #define ST_DEBUG */
+
+/* protocols */
+#define FAC_PROTO_ROP 0x11
+#define FAC_PROTO_CMIP 0x12
+#define FAC_PROTO_ACSE 0x13
+
+/* tag classes */
+#define FAC_TAGCLASS_UNI 0x00
+#define FAC_TAGCLASS_APW 0x01
+#define FAC_TAGCLASS_COS 0x02
+#define FAC_TAGCLASS_PRU 0x03
+
+/* tag forms */
+#define FAC_TAGFORM_PRI 0x00
+#define FAC_TAGFORM_CON 0x01
+
+/* class UNIVERSAL values */
+#define FAC_CODEUNI_BOOL 1
+#define FAC_CODEUNI_INT 2
+#define FAC_CODEUNI_BITS 3
+#define FAC_CODEUNI_OCTS 4
+#define FAC_CODEUNI_NULL 5
+#define FAC_CODEUNI_OBJI 6
+#define FAC_CODEUNI_OBJD 7
+#define FAC_CODEUNI_EXT 8
+#define FAC_CODEUNI_REAL 9
+#define FAC_CODEUNI_ENUM 10
+#define FAC_CODEUNI_R11 11
+#define FAC_CODEUNI_R12 12
+#define FAC_CODEUNI_R13 13
+#define FAC_CODEUNI_R14 14
+#define FAC_CODEUNI_R15 15
+#define FAC_CODEUNI_SEQ 16
+#define FAC_CODEUNI_SET 17
+#define FAC_CODEUNI_NSTR 18
+#define FAC_CODEUNI_PSTR 19
+#define FAC_CODEUNI_TSTR 20
+#define FAC_CODEUNI_VSTR 21
+#define FAC_CODEUNI_ISTR 22
+#define FAC_CODEUNI_UTIME 23
+#define FAC_CODEUNI_GTIME 24
+#define FAC_CODEUNI_GSTR 25
+#define FAC_CODEUNI_VISTR 26
+#define FAC_CODEUNI_GNSTR 27
+
+/* operation values */
+#define FAC_OPVAL_UUS 1
+#define FAC_OPVAL_CUG 2
+#define FAC_OPVAL_MCID 3
+#define FAC_OPVAL_BTPY 4
+#define FAC_OPVAL_ETPY 5
+#define FAC_OPVAL_ECT 6
+
+#define FAC_OPVAL_DIV_ACT 7
+#define FAC_OPVAL_DIV_DEACT 8
+#define FAC_OPVAL_DIV_ACTSN 9
+#define FAC_OPVAL_DIV_DEACTSN 10
+#define FAC_OPVAL_DIV_INTER 11
+#define FAC_OPVAL_DIV_INFO 12
+#define FAC_OPVAL_DIV_CALLDEF 13
+#define FAC_OPVAL_DIV_CALLRER 14
+#define FAC_OPVAL_DIV_LINF2 15
+#define FAC_OPVAL_DIV_INVS 16
+#define FAC_OPVAL_DIV_INTER1 17
+#define FAC_OPVAL_DIV_LINF1 18
+#define FAC_OPVAL_DIV_LINF3 19
+
+#define FAC_OPVAL_ER_CRCO 20
+#define FAC_OPVAL_ER_MGMT 21
+#define FAC_OPVAL_ER_CANC 22
+
+#define FAC_OPVAL_MLPP_QUERY 24
+#define FAC_OPVAL_MLPP_CALLR 25
+#define FAC_OPVAL_MLPP_CALLP 26
+
+#define FAC_OPVAL_AOC_REQ 30
+#define FAC_OPVAL_AOC_S_CUR 31
+#define FAC_OPVAL_AOC_S_SPC 32
+#define FAC_OPVAL_AOC_D_CUR 33
+#define FAC_OPVAL_AOC_D_UNIT 34
+#define FAC_OPVAL_AOC_E_CUR 35
+#define FAC_OPVAL_AOC_E_UNIT 36
+#define FAC_OPVAL_AOC_IDOFCRG 37
+
+#define FAC_OPVAL_CONF_BEG 40
+#define FAC_OPVAL_CONF_ADD 41
+#define FAC_OPVAL_CONF_SPLIT 42
+#define FAC_OPVAL_CONF_DROP 43
+#define FAC_OPVAL_CONF_ISOLATE 44
+#define FAC_OPVAL_CONF_REATT 45
+#define FAC_OPVAL_CONF_PDISC 46
+#define FAC_OPVAL_CONF_FCONF 47
+#define FAC_OPVAL_CONF_END 48
+#define FAC_OPVAL_CONF_IDCFE 49
+
+#define FAC_OPVAL_REVC_REQ 60
+
+enum states {
+ ST_EXP_COMP_TYP,
+ ST_EXP_INV_ID,
+ ST_EXP_OP_VAL,
+ ST_EXP_INFO,
+ ST_EXP_RUL,
+ ST_EXP_RU,
+ ST_EXP_RNOU,
+ ST_EXP_TOCI,
+ ST_EXP_DBID,
+
+ ST_EXP_RR_INV_ID,
+ ST_EXP_RR_OP_VAL,
+ ST_EXP_RR_RESULT,
+
+ ST_EXP_REJ_INV_ID,
+ ST_EXP_REJ_OP_VAL,
+ ST_EXP_REJ_RESULT,
+
+ ST_EXP_NIX
+};
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/isdndecode.8 b/usr.sbin/i4b/isdndecode/isdndecode.8
new file mode 100644
index 0000000..36c5b6f
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/isdndecode.8
@@ -0,0 +1,173 @@
+.\"
+.\" Copyright (c) 1998, 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdndecode.8,v 1.9 2000/02/21 15:17:17 hm Exp $
+.\"
+.\" last edit-date: [Mon Feb 21 16:15:09 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 21, 2000
+.Dt ISDNDECODE 8
+.Os
+.Sh NAME
+.Nm isdndecode
+.Nd isdn4bsd ISDN protocol decode utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl b
+.Op Fl d
+.Op Fl f Ar filename
+.Op Fl h
+.Op Fl i
+.Op Fl l
+.Op Fl o
+.Op Fl p Ar filename
+.Op Fl u Ar number
+.Op Fl x
+.Op Fl B
+.Op Fl P
+.Op Fl R Ar unit
+.Op Fl T Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility is part of the isdn4bsd package and is used to provide the user with a
+detailed mnemonic display of the layers 1, 2 and 3 protocol activities on
+the D channel and hex dump of the B channel(s) activities.
+.Pp
+Together with two passive supported cards and an easy to build cable it can
+also be used to monitor the complete traffic on a S0 bus providing S0 bus
+analyzer features.
+.Pp
+The
+.Nm
+utility is only available for passive supported cards.
+.Pp
+The following options can be used:
+.Bl -tag -width Ds
+.It Fl a
+Run
+.Nm
+in analyzer mode by using two passive cards and a custom cable which can
+be build as described in the file
+.Em cable.txt
+in the isdn4bsd source distribution. One card acts as a receiver for the
+transmitting direction on the S0 bus while the other card acts as a receiver
+for the receiving direction on the S0 bus. Complete traffic monitoring is
+possible using this setup.
+.It Fl b
+switch B channel tracing on (default off).
+.It Fl d
+switch D channel tracing off (default on).
+.It Fl f
+Use
+.Ar filename
+as the name of a file into which to write tracing output (default filename is
+isdndecode<n> where n is the number of the unit to decode).
+.It Fl h
+switch display of header off (default on).
+.It Fl i
+print layer 1 (I.430) INFO signals to monitor layer 1 activity (default off).
+.It Fl l
+switch displaying of Layer 2 (Q.921) frames off (default on).
+.It Fl o
+switch off writing decode output to a file (default on).
+.It Fl p
+Use
+.Ar filename
+as the name of a file used for the -B and -P options (default filename
+is isdntracebin<n> where n is the number of the unit to decode).
+.It Fl u
+Use
+.Ar number
+as the unit number of the controller card to decode (default 0).
+.It Fl x
+Decode Layer 3 packets with an unknown protocol discriminator.
+.It Fl B
+Write undecoded binary decode data to a file for later or remote
+analyzing (default off).
+.It Fl P
+Read undecoded binary decode data from file instead from device (default off).
+.It Fl R
+Use
+.Ar unit
+as the receiving interface unit number in analyze mode.
+.It Fl T
+Use
+.Ar unit
+as the transmitting interface unit number in analyze mode.
+.El
+.Pp
+When the USR1 signal is sent to a
+.Nm
+process, the currently used logfiles are reopened, so that logfile
+rotation becomes possible.
+.Pp
+The decode output should be obvious. It is very handy to have the following
+standard texts available when tracing ISDN protocols:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I.430
+ISDN BRI layer 1 protocol description.
+.It Ar Q.921
+ISDN D-channel layer 2 protocol description.
+.It Ar Q.931
+ISDN D-channel layer 3 protocol description.
+.El
+.Sh FILES
+.Bl -tag -width daddeldi -compact
+.It Pa /dev/i4btrc<n>
+The devicefile(s) used to get the decode messages for ISDN card unit <n>
+out of the kernel.
+.El
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdndecode -f /var/tmp/isdn.decode
+.Ed
+.Pp
+will start D channel tracing on passive controller 0 with all except B
+channel tracing enabled and logs everything into the output file
+/var/tmp/isdn.decode.
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh BUGS
+Still one left.
+.Sh STANDARDS
+ITU Recommendations I.430, Q.920, Q.921, Q.930, Q.931
+.Pp
+ITU Recommendation Q.932 (03/93), Q.950 (03/93)
+.Pp
+ETSI Recommendation ETS 300 179 (10/92), ETS 300 180 (10/92)
+.Pp
+ETSI Recommendation ETS 300 181 (04/93), ETS 300 182 (04/93)
+.Pp
+ITU Recommendation X.208, X.209
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdndecode/layer1.c b/usr.sbin/i4b/isdndecode/layer1.c
new file mode 100644
index 0000000..c08b628
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer1.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer1.c - isdndecode, decode and print layer 1 information
+ * -----------------------------------------------------------
+ *
+ * $Id: layer1.c,v 1.4 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:50:34 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+/*---------------------------------------------------------------------------*
+ * decode layer 1 information
+ *---------------------------------------------------------------------------*/
+void
+layer1(char *buffer, unsigned char *buf)
+{
+ switch(*buf)
+ {
+ case INFO0:
+ strcpy(buffer,"L1 INFO0 (No Signal)\n");
+ break;
+
+ case INFO1_8:
+ strcpy(buffer,"L1 INFO1 (Activation Request, Priority = 8)\n");
+ break;
+
+ case INFO1_10:
+ strcpy(buffer,"L1 INFO1 (Activation Request, Priority = 10)\n");
+ break;
+
+ case INFO2:
+ strcpy(buffer,"L1 INFO2 (Pending Activation)\n");
+ break;
+
+ case INFO3:
+ strcpy(buffer,"L1 INFO3 (Synchronized)\n");
+ break;
+
+ case INFO4_8:
+ strcpy(buffer,"L1 INFO4 (Activated, Priority = 8/9)\n");
+ break;
+
+ case INFO4_10:
+ strcpy(buffer,"L1 INFO4 (Activated, Priority = 10/11)\n");
+ break;
+
+ default:
+ sprintf(buffer,"L1 ERROR, invalid INFO value 0x%x!\n", *buf);
+ break;
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/layer2.c b/usr.sbin/i4b/isdndecode/layer2.c
new file mode 100644
index 0000000..1e53986
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer2.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer2.c - decode and print layer 2 (Q.921) information
+ * -------------------------------------------------------
+ *
+ * $Id: layer2.c,v 1.5 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:50:41 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+/*---------------------------------------------------------------------------*
+ * decode poll bit
+ *---------------------------------------------------------------------------*/
+static void
+poll(int layer, char *buffer, int cnt, unsigned char value, unsigned char mask)
+{
+ sprintline(layer, buffer, cnt, value, mask, "P/F, Poll = %s", (value & mask) ? "Immediate Response Required" : "No Immediate Response Required");
+}
+
+/*---------------------------------------------------------------------------*
+ * decode final bit
+ *---------------------------------------------------------------------------*/
+static void
+final(int layer, char *buffer, int cnt, unsigned char value, unsigned char mask)
+{
+ sprintline(layer, buffer, cnt, value, mask, "P/F, Final = %s", (value & mask) ? "Result of Poll" : "No Result of Poll");
+}
+
+/*---------------------------------------------------------------------------*
+ * decode protocol specified in Q.921
+ *---------------------------------------------------------------------------*/
+int
+layer2(char *pbuf, unsigned char *buf, int dir, int printit)
+{
+ int sap, tei, cmd;
+ int cnt = 0;
+ char locbuf[32000];
+ char *lbufp = &locbuf[0];
+ char buffer[80];
+
+ *lbufp = '\0';
+ *pbuf = '\0';
+
+ /* address high */
+
+ sap = (buf[0] >> 2) & 0x3f;
+
+ if(sap == 0)
+ strcpy(buffer, "Call Control");
+ else if((sap >= 1) && (sap <= 15))
+ strcpy(buffer, "Reserved");
+ else if(sap == 16)
+ strcpy(buffer, "X.25");
+ else if((sap >= 17) && (sap <= 31))
+ strcpy(buffer, "Reserved");
+ else if(sap == 63)
+ strcpy(buffer, "Layer 2 Management");
+ else
+ strcpy(buffer, "Not available for Q.921");
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[0], 0xfc, "SAPI = %d (%s)", sap, buffer);
+
+ if(dir == FROM_TE)
+ cmd = !(buf[0] & 0x02);
+ else
+ cmd = buf[0] & 0x02;
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[0], 0x02, "C/R = %s", cmd ? "Command" : "Response");
+ extension(2, lbufp+strlen(lbufp), cnt, buf[0], 0x01);
+ cnt++;
+
+ /* address low */
+
+ tei = buf[1] >> 1;
+
+ if((tei >= 0) && (tei <= 63))
+ strcpy(buffer, "Non-automatic TEI");
+ else if((tei >= 64) && (tei <= 126))
+ strcpy(buffer, "Automatic TEI");
+ if(tei == 127)
+ strcpy(buffer, "Group TEI");
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[1], 0xfe, "TEI = %d (%s)", tei, buffer);
+ extension(2, lbufp+strlen(lbufp), cnt, buf[1], 0x01);
+ cnt++;
+
+ /* control 1 */
+
+ if((buf[2] & 0x03) == 0x03)
+ {
+ /* U-frame */
+
+ if((buf[2] & 0xef) == 0x6f)
+ {
+ /* SABME */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: SABME (Set Asynchonous Balanced Mode)");
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x0f)
+ {
+ /* DM */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: DM (Disconnected Mode)");
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x03)
+ {
+ /* UI */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: UI (Unnumbered Information)");
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+
+ if(sap == 63 && (buf[3] == 0x0f)) /* TEI management */
+ {
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[3], 0xff, "MEI (Management Entity Identifier)");
+ cnt++;
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[4], 0xff, "Ri = 0x%04x (Reference number high)", (buf[4] << 8) | buf[5]);
+ cnt++;
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[5], 0xff, "Ri (Reference Number low)");
+ cnt++;
+
+ switch(buf[6])
+ {
+ case 0x01:
+ strcpy(buffer, "Identity Request");
+ break;
+ case 0x02:
+ strcpy(buffer, "Identity Assigned");
+ break;
+ case 0x03:
+ strcpy(buffer, "Identity denied");
+ break;
+ case 0x04:
+ strcpy(buffer, "Identity Check Request");
+ break;
+ case 0x05:
+ strcpy(buffer, "Identity Check Response");
+ break;
+ case 0x06:
+ strcpy(buffer, "Identity Remove");
+ break;
+ case 0x07:
+ strcpy(buffer, "Identity Verify");
+ break;
+ default:
+ strcpy(buffer, "undefined");
+ break;
+ }
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[6], 0xff, "TEI %s (Message Type %d)", buffer, buf[6]);
+ cnt++;
+
+ switch(buf[6])
+ {
+ case 0x01:
+ strcpy(buffer, "Any TEI value acceptable");
+ break;
+ case 0x02:
+ strcpy(buffer, "");
+ break;
+ case 0x03:
+ strcpy(buffer, "No TEI Value available");
+ break;
+ case 0x04:
+ strcpy(buffer, "Check all TEI values");
+ break;
+ case 0x05:
+ strcpy(buffer, "");
+ break;
+ case 0x06:
+ strcpy(buffer, "Request for removal of all TEI values");
+ break;
+ case 0x07:
+ strcpy(buffer, "");
+ break;
+ default:
+ strcpy(buffer, "");
+ break;
+ }
+ if(((buf[7] >> 1) & 0x7f) == 127)
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[7], 0xfe, "Ai = %d (Action Indicator = %s)", (buf[7] >> 1) & 0x7f, buffer);
+ else
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[7], 0xfe, "Ai = %d (Action Indicator)", (buf[7] >> 1) & 0x7f);
+ extension(2, lbufp+strlen(lbufp), cnt, buf[7], 0x01);
+ cnt++;
+ }
+ }
+ else if((buf[2] & 0xef) == 0x43)
+ {
+ /* DISC */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: DISC (Disconnect)");
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x63)
+ {
+ /* UA */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: UA (Unnumbered Acknowledge)");
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x87)
+ {
+ /* FRMR */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: FRMR (Frame Reject)");
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+ else if((buf[2] & 0xef) == 0x9f)
+ {
+ /* XID */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xef, "U-Frame: XID (Exchange Identification)");
+ if(cmd)
+ poll(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ else
+ final(2, lbufp+strlen(lbufp), cnt, buf[2], 0x10);
+ cnt++;
+ }
+
+ }
+ else if((buf[2] & 0x03) == 0x01)
+ {
+ /* S-frame */
+
+ if(buf[2] == 0x01)
+ strcpy(buffer, "RR (Receiver Ready)");
+ else if(buf[2] == 0x05)
+ strcpy(buffer, "RNR (Receiver Not Ready)");
+ else if(buf[2] == 0x09)
+ strcpy(buffer, "REJ (Reject)");
+ else
+ strcpy(buffer, "Unknown");
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xff, "S-Frame: %s", buffer);
+ cnt++;
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[3], 0xfe, "N(R) = %d (receive sequence number)", (buf[3] >> 1) & 0x7f);
+ if(cmd)
+ poll(2, lbufp+strlen(lbufp), cnt, buf[3], 0x01);
+ else
+ final(2, lbufp+strlen(lbufp), cnt, buf[3], 0x01);
+ cnt++;
+
+ }
+ else if((buf[2] & 0x01) == 0x00)
+ {
+ /* I-frame */
+
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0xfe, "N(S) = %d (send sequence number)", (buf[2] >> 1) & 0x7f);
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[2], 0x01, "I-Frame: Information transfer");
+ cnt++;
+
+ sprintf(buffer, "N(R) = %d", (buf[3] >> 1) & 0x7f);
+ sprintline(2, lbufp+strlen(lbufp), cnt, buf[3], 0xfe, "N(R) = %d (receive sequence number)", (buf[3] >> 1) & 0x7f);
+ poll(2, lbufp+strlen(lbufp), cnt, buf[3], 0x01);
+ cnt++;
+
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"%s", &locbuf[0]);
+ return (cnt);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/layer3.c b/usr.sbin/i4b/isdndecode/layer3.c
new file mode 100644
index 0000000..12a7633
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer3.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer3.c - decode and print layer 3 (Q.931) information
+ * -------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Mar 26 14:39:02 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+char *mttab[] = {
+
+/* 0x00 */ /* call establishment group */
+
+ "ESCAPE",
+ "ALERTING",
+ "CALL PROCEEDING",
+ "PROGRESS",
+ "undefined (0x04)",
+ "SETUP",
+ "undefined (0x06)",
+ "CONNECT",
+ "undefined (0x08)",
+ "undefined (0x09)",
+ "undefined (0x0a)",
+ "undefined (0x0b)",
+ "undefined (0x0c)",
+ "SETUP ACKNOWLEDGE",
+ "undefined (0x0e)",
+ "CONNECT ACKNOWLEDGE",
+
+/* 0x10 */
+ "undefined (0x10)",
+ "undefined (0x11)",
+ "undefined (0x12)",
+ "undefined (0x13)",
+ "undefined (0x14)",
+ "undefined (0x15)",
+ "undefined (0x16)",
+ "undefined (0x17)",
+ "undefined (0x18)",
+ "undefined (0x19)",
+ "undefined (0x1a)",
+ "undefined (0x1b)",
+ "undefined (0x1c)",
+ "undefined (0x1d)",
+ "undefined (0x1e)",
+ "undefined (0x1f)",
+
+/* 0x20 */
+
+ "USER INFORMATION", /* call information phase */
+ "SUSPEND REJECT",
+ "RESUME REJECT",
+ "undefined (0x23)",
+ "HOLD",
+ "SUSPEND",
+ "RESUME",
+ "undefined (0x27)",
+ "HOLD ACKNOWLEDGE",
+ "undefined (0x29)",
+ "undefined (0x2a)",
+ "undefined (0x2b)",
+ "undefined (0x2c)",
+ "SUSPEND ACKNOWLEDGE",
+ "RESUME ACKNOWLEDGE",
+ "undefined (0x2f)",
+
+/* 0x30 */
+
+ "HOLD REJECT",
+ "RETRIEVE",
+ "undefined (0x32)",
+ "RETRIEVE ACKNOWLEDGE",
+ "undefined (0x34)",
+ "undefined (0x35)",
+ "undefined (0x36)",
+ "RETRIEVE REJECT",
+ "undefined (0x38)",
+ "undefined (0x39)",
+ "undefined (0x3a)",
+ "undefined (0x3b)",
+ "undefined (0x3c)",
+ "undefined (0x3d)",
+ "undefined (0x3e)",
+ "undefined (0x3f)",
+
+/* 0x40 */
+
+ "DETACH", /* call clearing */
+ "undefined (0x41)",
+ "undefined (0x42)",
+ "undefined (0x43)",
+ "undefined (0x44)",
+ "DISCONNECT",
+ "RESTART",
+ "undefined (0x47)",
+ "DETACH ACKNOWLEDGE",
+ "undefined (0x49)",
+ "undefined (0x4a)",
+ "undefined (0x4b)",
+ "undefined (0x4c)",
+ "RELEASE",
+ "RESTART ACKNOWLEDGE",
+ "undefined (0x4f)",
+
+/* 0x50 */
+
+ "undefined (0x50)",
+ "undefined (0x51)",
+ "undefined (0x52)",
+ "undefined (0x53)",
+ "undefined (0x54)",
+ "undefined (0x55)",
+ "undefined (0x56)",
+ "undefined (0x57)",
+ "undefined (0x58)",
+ "undefined (0x59)",
+ "RELEASE COMPLETE",
+ "undefined (0x5b)",
+ "undefined (0x5c)",
+ "undefined (0x5d)",
+ "undefined (0x5e)",
+ "undefined (0x5f)",
+
+/* 0x60 */
+
+ "SEGMENT", /* misc messages */
+ "undefined (0x61)",
+ "FACILITY",
+ "undefined (0x63)",
+ "REGISTER",
+ "undefined (0x65)",
+ "undefined (0x66)",
+ "undefined (0x67)",
+ "CANCEL ACKNOWLEDGE",
+ "undefined (0x69)",
+ "FACILITY ACKNOWLEDGE",
+ "undefined (0x6b)",
+ "REGISTER ACKNOWLEDGE",
+ "undefined (0x6d)",
+ "NOTIFY",
+ "undefined (0x6f)",
+
+/* 0x70 */
+
+ "CANCEL REJECT",
+ "undefined (0x71)",
+ "FACILITY REJECT",
+ "undefined (0x73)",
+ "REGISTER REJECT",
+ "STATUS ENQIRY",
+ "undefined (0x76)",
+ "undefined (0x77)",
+ "undefined (0x78)",
+ "CONGESTION CONTROL",
+ "undefined (0x7a)",
+ "INFORMATION",
+ "undefined (0x7c)",
+ "STATUS",
+ "undefined (0x7e)",
+ "undefined (0x7f)",
+};
+
+#define MTTAB_MAX 0x7f
+
+extern int f_null(char *pbuf, unsigned char *buf, int off);
+extern int f_bc(char *pbuf, unsigned char *buf, int off);
+extern int f_cause(char *pbuf, unsigned char *buf, int off);
+extern int f_cstat(char *pbuf, unsigned char *buf, int off);
+extern int f_chid(char *pbuf, unsigned char *buf, int off);
+extern int f_fac(char *pbuf, unsigned char *buf, int off);
+extern int f_progi(char *pbuf, unsigned char *buf, int off);
+extern int f_displ(char *pbuf, unsigned char *buf, int off);
+extern int f_date(char *pbuf, unsigned char *buf, int off);
+extern int f_cnu(char *pbuf, unsigned char *buf, int off);
+extern int f_cgpn(char *pbuf, unsigned char *buf, int off);
+extern int f_cdpn(char *pbuf, unsigned char *buf, int off);
+extern int f_hlc(char *pbuf, unsigned char *buf, int off);
+extern int f_uu(char *pbuf, unsigned char *buf, int off);
+
+struct ie {
+ unsigned char code; /* information element identifier code */
+ char *name; /* ie name */
+ int (*func) (char *pbuf, unsigned char *buf, int off); /* decode function */
+} ietab[] = {
+ { 0x00, "segmented message", f_null },
+ { 0x04, "bearer capability", f_bc },
+ { 0x08, "cause", f_cause },
+ { 0x0c, "connected address", f_null },
+ { 0x0d, "extended facility", f_null },
+ { 0x10, "call identity", f_null },
+ { 0x14, "call state", f_cstat },
+ { 0x18, "channel id", f_chid },
+ { 0x19, "data link connection id", f_null },
+ { 0x1c, "facility", f_fac },
+ { 0x1e, "progress indicator", f_progi },
+ { 0x20, "network specific facilities", f_null },
+ { 0x24, "terminal capabilities", f_null },
+ { 0x27, "notification indicator", f_null },
+ { 0x28, "display", f_displ },
+ { 0x29, "date/time", f_date },
+ { 0x2c, "keypad", f_null },
+ { 0x30, "keypad echo", f_null },
+ { 0x32, "information request", f_null },
+ { 0x34, "signal", f_null },
+ { 0x36, "switchhook", f_null },
+ { 0x38, "feature activation", f_null },
+ { 0x39, "feature indication", f_null },
+ { 0x3a, "service profile id", f_null },
+ { 0x3b, "endpoint identifier", f_null },
+ { 0x40, "information rate", f_null },
+ { 0x41, "precedence level", f_null },
+ { 0x42, "end-to-end transit delay", f_null },
+ { 0x43, "transit delay detection", f_null },
+ { 0x44, "packet layer binary parms", f_null },
+ { 0x45, "packet layer window size", f_null },
+ { 0x46, "packet size", f_null },
+ { 0x47, "closed user group", f_null },
+ { 0x48, "link layer core parameters", f_null },
+ { 0x49, "link layer protocol parms", f_null },
+ { 0x4a, "reverse charging information", f_null },
+ { 0x4c, "connected number", f_cnu },
+ { 0x4d, "connected subaddress", f_null },
+ { 0x50, "X.213 priority", f_null },
+ { 0x51, "report type", f_null },
+ { 0x53, "link integrity verification", f_null },
+ { 0x57, "PVC status", f_null },
+ { 0x6c, "calling party number", f_cnu },
+ { 0x6d, "calling party subaddress", f_null },
+ { 0x70, "called party number", f_cnu },
+ { 0x71, "called party subaddress", f_null },
+ { 0x74, "redirecting number", f_null },
+ { 0x78, "transit network selection", f_null },
+ { 0x79, "restart indicator", f_null },
+ { 0x7c, "low layer compatibility", f_null },
+ { 0x7d, "high layer compatibility", f_hlc },
+ { 0x7e, "user-user", f_uu },
+ { 0x7f, "escape for extension", f_null },
+ { 0xff, "unknown information element", f_null }
+};
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931 protocol
+ *---------------------------------------------------------------------------*/
+void
+layer3(char *pbuf, int n, int off, unsigned char *buf)
+{
+ char buffer[256];
+ int codeset = 0;
+ int codelock = 0;
+ int oldcodeset = 0;
+
+ int pd;
+ int len;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ i = 0;
+
+ /* protocol discriminator */
+
+ pd = buf[i];
+
+ if(pd >= 0x00 && pd <= 0x07)
+ sprintf(buffer, "User-User IE (0x%02x)",pd);
+ else if(pd == 0x08)
+ sprintf(buffer, "Q.931/I.451");
+ else if(pd >= 0x10 && pd <= 0x3f)
+ sprintf(buffer, "Other Layer 3 or X.25 (0x%02x)",pd);
+ else if(pd >= 0x40 && pd <= 0x4f)
+ sprintf(buffer, "National Use (0x%02x)",pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf(buffer, "Other Layer 3 or X.25 (0x%02x)",pd);
+ else
+ sprintf(buffer, "Reserved (0x%02x)",pd);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, pd, 0xff, "Protocol discriminator = %s", buffer);
+ i++;
+
+ if(pd != 0x08)
+ {
+ for (; i < n;i++)
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "-");
+ return;
+ }
+
+ /* call reference */
+
+ len = buf[i] & 0x0f;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xf0, "Call Reference");
+
+ switch(len)
+ {
+ case 0:
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Length of Call Reference = 0 (Dummy CR)");
+ break;
+ case 1:
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Length of Call Reference = 1");
+ i++;
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "Call Reference sent %s origination side", (buf[i] & 0x80) ? "to" : "from");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Call Reference = %d = 0x%02x", (buf[i] & 0x7f), (buf[i] & 0x7f));
+ break;
+ case 2:
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Length of Call Reference = 2");
+ i++;
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "Call Reference sent %s origination side", (buf[i] & 0x80) ? "to" : "from");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Call reference = %d = %02x", (buf[i] & 0x7f), (buf[i] & 0x7f));
+ i++;
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Call reference = %d = %02x", (buf[i]), (buf[i]));
+ break;
+ }
+ i++;
+
+ /* message type */
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "Message type extension = %d", buf[i] & 0x80 ? 1 : 0);
+
+ if(buf[i] <= MTTAB_MAX)
+ strcpy(buffer, mttab[buf[i]]);
+ else
+ sprintf(buffer, "unknown (0x%02x)", buf[i]);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Message type = %s", buffer);
+ i++;
+
+ /* information elements */
+
+ for (; i < n;)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x80, "%s Information element", buf[i] & 0x80 ? "Single octet" : "Variable length");
+
+ if(buf[i] & 0x80)
+ {
+ /* single octett info element type 1 */
+
+ if((buf[i] & 0x70) == 0x00)
+ {
+ strcpy(buffer, "Reserved");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Reserved");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Reserved, content of IE");
+ }
+ else if((buf[i] & 0x70) == 0x10)
+ {
+ strcpy(buffer, "Shift");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Shift");
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x08, "%s shift", buf[i] & 0x08 ? "Non-locking" : "Locking");
+
+ switch(buf[i] & 0x07)
+ {
+ case 0:
+ strcpy(buffer, "Not applicable");
+ break;
+ case 1:
+ case 2:
+ case 3:
+ sprintf(buffer, "Reserved (%d)", buf[i] & 0x07);
+ break;
+ case 4:
+ strcpy(buffer, "Codeset 4 (ISO/IEC)");
+ break;
+ case 5:
+ strcpy(buffer, "Codeset 5 (National use)");
+ break;
+ case 6:
+ strcpy(buffer, "Codeset 6 (Local network specific)");
+ break;
+ case 7:
+ strcpy(buffer, "Codeset 7 (User specific)");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x07, "%s", buffer);
+ break;
+ }
+ else if((buf[i] & 0x70) == 0x30)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Congestion Level");
+ switch(buf[i] & 0x0f)
+ {
+ case 0x00:
+ strcpy(buffer, "receiver ready");
+ break;
+ case 0x0f:
+ strcpy(buffer, "receiver not ready");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Congestion Level = ", buffer);
+ break;
+ }
+ else if((buf[i] & 0x70) == 0x50)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x70, "Repeat Indicator");
+ switch(buf[i] & 0x0f)
+ {
+ case 0x02:
+ strcpy(buffer, "Prioritized list for selecting one possibility");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Repeat indication = ", buffer);
+ break;
+ }
+
+ /* single octett info element type 2 */
+
+ else if((buf[i] & 0x7f) == 0x20)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "More data");
+ }
+ else if((buf[i] & 0x7f) == 0x21)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Sending complete");
+ }
+ else
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "UNKNOWN single octet IE = 0x%02x", buf[i]);
+ }
+ i++; /* next */
+ }
+ else
+ {
+ if(codeset == 0)
+ {
+ struct ie *iep = &ietab[0];
+
+ for(;;)
+ {
+ if((iep->code == buf[i]) ||
+ (iep->code == 0xff))
+ break;
+ iep++;
+ }
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "IE = %s", iep->name);
+ sprintline(3, pbuf+strlen(pbuf), off+i+1, buf[i+1], 0xff, "IE Length = %d", buf[i+1]);
+
+ if(iep->func == f_null)
+ {
+ }
+ else
+ {
+ i += (iep->func)(pbuf, &buf[i], off+i);
+ goto next;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "UNKNOWN CODESET=%d, IE=0x%02x", codeset, buf[i]);
+ }
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ sprintf((pbuf+strlen(pbuf)), "LEN=0x%02x, DATA=", len);
+
+ i++; /* index -> 1st param */
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"0x%02x ", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]\n");
+
+ i += len;
+
+next:
+
+ if(!codelock && (codeset != oldcodeset))
+ codeset = oldcodeset;
+ }
+ }
+/* sprintf((pbuf+strlen(pbuf)),"\n"); */
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/layer3_subr.c b/usr.sbin/i4b/isdndecode/layer3_subr.c
new file mode 100644
index 0000000..6fcedd2
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer3_subr.c
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer3_subr.c - subroutines for IE decoding
+ * -------------------------------------------
+ *
+ * $Id: layer3_subr.c,v 1.8 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 15:45:16 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+
+/*---------------------------------------------------------------------------*
+ * dummy function
+ *---------------------------------------------------------------------------*/
+int
+f_null(char *pbuf, unsigned char *buf, int off)
+{
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_cstat(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "Special");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", State=");
+
+ switch((buf[i] & 0x3f))
+ {
+ case 0:
+ strcpy(buffer, "Null");
+ break;
+ case 1:
+ strcpy(buffer, "Call initiated");
+ break;
+ case 2:
+ strcpy(buffer, "Overlap sending");
+ break;
+ case 3:
+ strcpy(buffer, "Outgoing call proceeding");
+ break;
+ case 4:
+ strcpy(buffer, "Call delivered");
+ break;
+ case 6:
+ strcpy(buffer, "Call present");
+ break;
+ case 7:
+ strcpy(buffer, "Call received");
+ break;
+ case 8:
+ strcpy(buffer, "Connect request");
+ break;
+ case 9:
+ strcpy(buffer, "Incoming call proceeding");
+ break;
+ case 10:
+ strcpy(buffer, "Active");
+ break;
+ case 11:
+ strcpy(buffer, "Disconnect request");
+ break;
+ case 12:
+ strcpy(buffer, "Disconnect indication");
+ break;
+ case 15:
+ strcpy(buffer, "Suspend request");
+ break;
+ case 17:
+ strcpy(buffer, "Resume request");
+ break;
+ case 19:
+ strcpy(buffer, "Release request");
+ break;
+ case 22:
+ strcpy(buffer, "Call abort");
+ break;
+ case 25:
+ strcpy(buffer, "Overlap receiving");
+ break;
+ case 0x3d:
+ strcpy(buffer, "Restart request");
+ break;
+ case 0x3e:
+ strcpy(buffer, "Restart");
+ break;
+ default:
+ strcpy(buffer, "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_chid(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+ i++;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x40, "Interface Id present = %s", buf[i] & 0x40 ? "Yes" : "No");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x20, "Interface Type = %s", buf[i] & 0x20 ? "Other (PRI)" : "BRI");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x10, "Spare");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x08, "Channel = %s", buf[i] & 0x08 ? "exclusive" : "preferred");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x04, "Channel is%s the D-Channel", buf[i] & 0x04 ? "" : " not");
+
+ switch(buf[i] & 0x03)
+ {
+ case 0:
+ strcpy(buffer, "no channel");
+ break;
+ case 1:
+ strcpy(buffer, "B-1");
+ break;
+ case 2:
+ strcpy(buffer, "B-2");
+ break;
+ case 3:
+ strcpy(buffer, "any channel");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x03, "Channel = %s", buffer);
+
+ i++;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_fac(char *pbuf, unsigned char *buf, int off)
+{
+ return(q932_facility(pbuf, buf));
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_progi(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "Local");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", Loc=");
+
+ switch((buf[i] & 0x0f))
+ {
+ case 0:
+ strcpy(buffer, "User");
+ break;
+ case 1:
+ strcpy(buffer, "Private network serving local user");
+ break;
+ case 2:
+ strcpy(buffer, "Public network serving local user");
+ break;
+ case 3:
+ strcpy(buffer, "Transit network");
+ break;
+ case 4:
+ strcpy(buffer, "Public network serving remote user");
+ break;
+ case 5:
+ strcpy(buffer, "Private network serving remote user");
+ break;
+ case 6:
+ strcpy(buffer, "Network beyond interworking point");
+ break;
+ default:
+ strcpy(buffer, "ERROR: undefined/reserved");
+ break;
+ }
+
+ i++;
+
+ sprintf((pbuf+strlen(pbuf)), "\n Description");
+
+ switch((buf[i] & 0x7f))
+ {
+ case 1:
+ strcpy(buffer, "Call is not end-to-end ISDN");
+ break;
+ case 2:
+ strcpy(buffer, "Destination address is non-ISDN");
+ break;
+ case 3:
+ strcpy(buffer, "Origination address is non-ISDN");
+ break;
+ case 4:
+ strcpy(buffer, "Call has returned to the ISDN");
+ break;
+ case 5:
+ strcpy(buffer, "Interworking occured, Service change");
+ break;
+ case 8:
+ strcpy(buffer, "In-band info or appropriate pattern now available");
+ break;
+ default:
+ strcpy(buffer, "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_displ(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int j = 0;
+ int len = 0;
+
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+f_date(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int j = 0;
+ int len = 0;
+
+ i++;
+ len = buf[i];
+ i++;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Year = %02d", buf[i]);
+ i++;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Month = %02d", buf[i]);
+ i++;
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Day = %02d", buf[i]);
+ i++;
+
+ j=3;
+ if(j < len)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Hour = %02d", buf[i]);
+ i++;
+ j++;
+ }
+ if(j < len)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Minute = %02d", buf[i]);
+ i++;
+ j++;
+ }
+ if(j < len)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Second = %02d", buf[i]);
+ i++;
+ j++;
+ }
+ i += len;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the cause
+ *---------------------------------------------------------------------------*/
+int
+f_cause(char *pbuf, unsigned char *buf, int off)
+{
+ int j;
+ int len;
+ int i = 0;
+ int ls;
+ char buffer[256];
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++; /* coding/location */
+ len--;
+
+ ls = buf[i];
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((ls & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "Local");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Coding Standard = %s", buffer);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x10, "Spare");
+
+ switch(ls & 0x0f)
+ {
+ case 0x00:
+ strcpy(buffer, "user");
+ break;
+ case 0x01:
+ strcpy(buffer, "private network serving local user");
+ break;
+ case 0x02:
+ strcpy(buffer, "public network serving local user");
+ break;
+ case 0x03:
+ strcpy(buffer, "transit network");
+ break;
+ case 0x04:
+ strcpy(buffer, "public network serving remote user");
+ break;
+ case 0x05:
+ strcpy(buffer, "private network serving remote user");
+ break;
+ case 0x07:
+ strcpy(buffer, "international network");
+ break;
+ case 0x0a:
+ strcpy(buffer, "network beyond interworking point");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", ls & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Location = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!(ls & 0x80))
+ {
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0:
+ strcpy(buffer, "Q.931");
+ break;
+ case 3:
+ strcpy(buffer, "X.21");
+ break;
+ case 4:
+ strcpy(buffer, "X.25");
+ break;
+ case 5:
+ strcpy(buffer, "Q.1031/Q.1051");
+ break;
+ default:
+ strcpy(buffer, "Reserved");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Recommendation = %s", buffer);
+ i++;
+ len--;
+ }
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Cause = %s", print_cause_q850(buf[i] & 0x7f));
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "Diagnostics = %02d %s", buf[i+j], buf[i+j]);
+
+ i += (len+1);
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the bearer capability
+ *---------------------------------------------------------------------------*/
+int
+f_bc(char *pbuf, unsigned char *buf, int off)
+{
+ int len;
+ int i = 0;
+ int mr = 0;
+ char buffer[256];
+
+ i++; /* index -> length */
+
+ len = buf[i];
+ i++;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "CCITT");
+ break;
+ case 1:
+ strcpy(buffer, "ISO/IEC");
+ break;
+ case 2:
+ strcpy(buffer, "National");
+ break;
+ case 3:
+ strcpy(buffer, "NSI Std");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Coding standard = %s", buffer);
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ strcpy(buffer, "speech");
+ break;
+ case 0x08:
+ strcpy(buffer, "unrestricted digital information");
+ break;
+ case 0x09:
+ strcpy(buffer, "restricted digital information");
+ break;
+ case 0x10:
+ strcpy(buffer, "3.1 kHz audio");
+ break;
+ case 0x11:
+ strcpy(buffer, "unrestricted digital information with tones");
+ break;
+ case 0x18:
+ strcpy(buffer, "video");
+ break;
+ default:
+ sprintf(buffer, "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1f, "Capability = %s", buffer);
+
+ i++;
+ len--;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "circuit");
+ break;
+ case 2:
+ strcpy(buffer, "packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", ((buf[i] & 0x60) >> 5));
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Mode = %s", buffer);
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ strcpy(buffer, "packet mode");
+ break;
+ case 0x10:
+ strcpy(buffer, "64 kbit/s");
+ break;
+ case 0x11:
+ strcpy(buffer, "2 x 64 kbit/s");
+ break;
+ case 0x13:
+ strcpy(buffer, "384 kbit/s");
+ break;
+ case 0x15:
+ strcpy(buffer, "1536 kbit/s");
+ break;
+ case 0x17:
+ strcpy(buffer, "1920 kbit/s");
+ break;
+ case 0x18:
+ strcpy(buffer, "Multirate");
+ mr = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1f, "Rate = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ if(mr)
+ {
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Rate multiplier = %d", buf[i] & 0x7f);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x01:
+ strcpy(buffer, "V.110/X.30");
+ break;
+ case 0x02:
+ strcpy(buffer, "G.711 u-Law");
+ break;
+ case 0x03:
+ strcpy(buffer, "G.711 a-Law");
+ break;
+ case 0x04:
+ strcpy(buffer, "G.721 ADPCM/I.460");
+ break;
+ case 0x05:
+ strcpy(buffer, "H.221/H.242");
+ break;
+ case 0x07:
+ strcpy(buffer, "non-CCITT rate adaption");
+ break;
+ case 0x08:
+ strcpy(buffer, "V.120");
+ break;
+ case 0x09:
+ strcpy(buffer, "X.31");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x1f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1f, "Layer 1 Protocol = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+/* work to do ahead !!! */
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n user rate=0x%02x ", buf[i] & 0x1f);
+
+ if(buf[i] & 0x40)
+ sprintf((pbuf+strlen(pbuf)), "(async,");
+ else
+ sprintf((pbuf+strlen(pbuf)), "(sync,");
+
+ if(buf[i] & 0x20)
+ sprintf((pbuf+strlen(pbuf)), "in-band neg. possible)");
+ else
+ sprintf((pbuf+strlen(pbuf)), "in-band neg not possible)");
+
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n clk/flow=0x%02x", buf[i] & 0x1f);
+
+ sprintf((pbuf+strlen(pbuf)), "\n intermediate rate=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "not used");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "8 kbit/s");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "16 kbit/s");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "32 kbit/s");
+ break;
+ }
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n hdr/mfrm/etc.=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n stop/data/parity=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n modemtype=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x42:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=Q.921/I.441");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=X.25 link");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=Q.921/I.441");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=X.25 packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+exit:
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the ISDN (telephone) number
+ *---------------------------------------------------------------------------*/
+int
+f_cnu(char *pbuf, unsigned char *buf, int off)
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+ int ind = 0;
+ char buffer[256];
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> type/plan */
+ tp = buf[i];
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ strcpy(buffer, "Unknown");
+ break;
+ case 1:
+ strcpy(buffer, "International number");
+ break;
+ case 2:
+ strcpy(buffer, "National number");
+ break;
+ case 3:
+ strcpy(buffer, "Network specific number");
+ break;
+ case 4:
+ strcpy(buffer, "Subscriber number");
+ break;
+ case 6:
+ strcpy(buffer, "Abbreviated number");
+ break;
+ default:
+ sprintf(buffer, "Reserved (%d), ", ((tp & 0x70) >> 4));
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Type = %s", buffer);
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ strcpy(buffer, "Unknown");
+ break;
+ case 1:
+ strcpy(buffer, "ISDN (E.164)");
+ break;
+ case 3:
+ strcpy(buffer, "Data (X.121)");
+ break;
+ case 4:
+ strcpy(buffer, "Telex (F.69)");
+ break;
+ case 8:
+ strcpy(buffer, "National");
+ break;
+ case 9:
+ strcpy(buffer, "Private");
+ break;
+ default:
+ sprintf(buffer, "Reserved (%d)", (tp & 0x0f));
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x0f, "Plan = %s", buffer);
+
+ i++;
+ len--;
+
+ if(!(tp & 0x80))
+ {
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ strcpy(buffer, "allowed");
+ break;
+ case 1:
+ strcpy(buffer, "restricted");
+ break;
+ case 2:
+ strcpy(buffer, "number not available");
+ break;
+ case 3:
+ strcpy(buffer, "reserved");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Presentation = %s", buffer);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1c, "Spare");
+
+ switch(ind & 0x03)
+ {
+ case 0:
+ strcpy(buffer, "user provided, not screened");
+ break;
+ case 1:
+ strcpy(buffer, "user provided, verified & passed");
+ break;
+ case 2:
+ strcpy(buffer, "user provided, verified & failed");
+ break;
+ case 3:
+ strcpy(buffer, "network provided");
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x03, "Screening = %s", buffer);
+ i++;
+ len--;
+ }
+
+ for(j = 0; j < len; j++)
+ {
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "Number digit = %c", buf[i+j]);
+ }
+
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print HL comatibility
+ *---------------------------------------------------------------------------*/
+int
+f_hlc(char *pbuf, unsigned char *buf, int off)
+{
+ int i = 0;
+ int len = 0;
+ char buffer[256];
+
+ i++;
+ len = buf[i];
+
+ i++;
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch((buf[i] >> 5) & 0x03)
+ {
+ case 0: strcpy(buffer, "CCITT");
+ break;
+ case 1: strcpy(buffer, "ISO/IEC");
+ break;
+ case 2: strcpy(buffer, "National");
+ break;
+ case 3: strcpy(buffer, "Network");
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x60, "Coding standard = %s", buffer);
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x1c, "Interpretation = %s", ((buf[i] >> 2) & 0x07) == 0x04 ? "first" : "reserved");
+
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x03, "Presentation = %s", ((buf[i]) & 0x03) == 0x01 ? "High layer protocol profile" : "reserved");
+
+ i++;
+ len--;
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x01:
+ strcpy(buffer, "Telephony");
+ break;
+ case 0x04:
+ strcpy(buffer, "Fax Group 2/3 (F.182)");
+ break;
+ case 0x21:
+ strcpy(buffer, "Fax Group 4 I (F.184)");
+ break;
+ case 0x24:
+ strcpy(buffer, "Teletex (F.230) or Fax Group 4 II/III (F.184)");
+ break;
+ case 0x28:
+ strcpy(buffer, "Teletex (F.220)");
+ break;
+ case 0x31:
+ strcpy(buffer, "Teletex (F.200)");
+ break;
+ case 0x32:
+ strcpy(buffer, "Videotex (F.300/T.102)");
+ break;
+ case 0x33:
+ strcpy(buffer, "Videotex (F.300/T.101)");
+ break;
+ case 0x35:
+ strcpy(buffer, "Telex (F.60)");
+ break;
+ case 0x38:
+ strcpy(buffer, "MHS (X.400)");
+ break;
+ case 0x41:
+ strcpy(buffer, "OSI (X.200)");
+ break;
+ case 0x5e:
+ strcpy(buffer, "Maintenance");
+ break;
+ case 0x5f:
+ strcpy(buffer, "Management");
+ break;
+ case 0x60:
+ strcpy(buffer, "Audio visual (F.721)");
+ break;
+ default:
+ sprintf(buffer, "Reserved (0x%02x)", buf[i] & 0x7f);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Characteristics = %s", buffer);
+ i++;
+ len--;
+
+ if(buf[i-1] & 0x80)
+ {
+ return(i);
+ }
+
+ extension(3, pbuf+strlen(pbuf), off+i, buf[i], 0x80);
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x01:
+ strcpy(buffer, "Telephony");
+ break;
+ case 0x04:
+ strcpy(buffer, "Fax Group 2/3 (F.182)");
+ break;
+ case 0x21:
+ strcpy(buffer, "Fax Group 4 I (F.184)");
+ break;
+ case 0x24:
+ strcpy(buffer, "Teletex (F.230) or Fax Group 4 II/III (F.184)");
+ break;
+ case 0x28:
+ strcpy(buffer, "Teletex (F.220)");
+ break;
+ case 0x31:
+ strcpy(buffer, "Teletex (F.200)");
+ break;
+ case 0x32:
+ strcpy(buffer, "Videotex (F.300/T.102)");
+ break;
+ case 0x33:
+ strcpy(buffer, "Videotex (F.300/T.101)");
+ break;
+ case 0x35:
+ strcpy(buffer, "Telex (F.60)");
+ break;
+ case 0x38:
+ strcpy(buffer, "MHS (X.400)");
+ break;
+ case 0x41:
+ strcpy(buffer, "OSI (X.200)");
+ break;
+ case 0x5e:
+ strcpy(buffer, "Maintenance");
+ break;
+ case 0x5f:
+ strcpy(buffer, "Management");
+ break;
+ case 0x60:
+ strcpy(buffer, "Audio visual (F.721)");
+ break;
+ default:
+ sprintf(buffer, "Reserved (0x%02x)", buf[i] & 0x7f);
+ break;
+
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0x7f, "Ext. characteristics = %s", buffer);
+ i++;
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * user-user
+ *---------------------------------------------------------------------------*/
+int
+f_uu(char *pbuf, unsigned char *buf, int off)
+{
+ int j;
+ int len;
+ int i = 0;
+ int pd;
+ char buffer[256];
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> PD */
+ pd = buf[i];
+
+ switch(pd)
+ {
+ case 0:
+ strcpy(buffer, "user-specific");
+ break;
+ case 1:
+ strcpy(buffer, "OSI high layer");
+ break;
+ case 2:
+ strcpy(buffer, "X.244");
+ break;
+ case 3:
+ strcpy(buffer, "reserved for sys mgmt");
+ break;
+ case 4:
+ strcpy(buffer, "IA5 characters");
+ break;
+ case 5:
+ strcpy(buffer, "X.208/X.209");
+ break;
+ case 7:
+ strcpy(buffer, "V.120");
+ break;
+ case 8:
+ strcpy(buffer, "Q.931/I.451");
+ break;
+ default:
+ if(pd >= 0x10 && pd <= 0x3f)
+ sprintf(buffer, "reserved incl X.31 (0x%2x)", pd);
+ else if (pd >= 0x40 && pd <= 0x4f)
+ sprintf(buffer, "national use (0x%2x)", pd);
+ else if (pd >= 0x50 && pd <= 0xfe)
+ sprintf(buffer, "reserved incl X.31 (0x%2x)", pd);
+ else
+ sprintf(buffer, "reserved (0x%2x)", pd);
+ break;
+ }
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "protocol = %s", buffer);
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ {
+ if(isprint(buf[i+j]))
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "user information = %c", buf[i+j]);
+ else
+ sprintline(3, (pbuf+strlen(pbuf)), off+i+j, buf[i+j], 0xff, "user information = 0x%2x", buf[i+j]);
+ }
+
+ i += j;
+
+ return(i);
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/main.c b/usr.sbin/i4b/isdndecode/main.c
new file mode 100644
index 0000000..76afab1
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/main.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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.c - isdndecode main program file
+ * -------------------------------------
+ *
+ * $Id: main.c,v 1.13 2000/02/21 15:17:17 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 21 16:19:30 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+#include <unistd.h>
+
+unsigned char buf[BSIZE];
+FILE *Fout = NULL;
+FILE *BP = NULL;
+int outflag = 1;
+int header = 1;
+int print_q921 = 1;
+int unit = 0;
+int dchan = 0;
+int bchan = 0;
+int traceon = 0;
+int analyze = 0;
+int Rx = RxUDEF;
+int Tx = TxUDEF;
+int f;
+int Bopt = 0;
+int Popt = 0;
+int bpopt = 0;
+int info = 0;
+int xflag = 0;
+
+int enable_trace = TRACE_D_RX | TRACE_D_TX;
+
+static char outfilename[1024];
+static char BPfilename[1024];
+
+static void dumpbuf( int n, unsigned char *buf, i4b_trace_hdr_t *hdr);
+static int switch_driver( int value, int rx, int tx );
+static void usage( void );
+static void exit_hdl( void );
+static void reopenfiles( int );
+
+
+/*---------------------------------------------------------------------------*
+ * usage intructions
+ *---------------------------------------------------------------------------*/
+void
+usage(void)
+{
+ fprintf(stderr,"\n");
+ fprintf(stderr,"isdndecode - isdn4bsd package ISDN decoder for passive cards (%d.%d.%d)\n", VERSION, REL, STEP);
+ fprintf(stderr,"usage: isdntrace -a -b -d -f <file> -h -i -l -n <val> -o -p <file> -r -u <unit>\n");
+ fprintf(stderr," -x -B -P -R <unit> -T <unit>\n");
+ fprintf(stderr," -a analyzer mode ................................... (default off)\n");
+ fprintf(stderr," -b switch B channel trace on ....................... (default off)\n");
+ fprintf(stderr," -d switch D channel trace off ....................... (default on)\n");
+ fprintf(stderr," -f <file> write output to file filename ........... (default %s0)\n", DECODE_FILE_NAME);
+ fprintf(stderr," -h don't print header for each message ............. (default off)\n");
+ fprintf(stderr," -i print I.430 (layer 1) INFO signals .............. (default off)\n");
+ fprintf(stderr," -l don't decode low layer Q.921 messages ........... (default off)\n");
+ fprintf(stderr," -o don't write output to a file .................... (default off)\n");
+ fprintf(stderr," -p <file> specify filename for -B and -P ........ (default %s0)\n", BIN_FILE_NAME);
+ fprintf(stderr," -u <unit> specify controller unit number ............... (default unit 0)\n");
+ fprintf(stderr," -x print packets with unknown protocoldiscriminator (default off)\n");
+ fprintf(stderr," -B write binary trace data to file filename ........ (default off)\n");
+ fprintf(stderr," -P playback from binary trace data file ............ (default off)\n");
+ fprintf(stderr," -R <unit> analyze Rx controller unit number (for -a) ... (default unit %d)\n", RxUDEF);
+ fprintf(stderr," -T <unit> analyze Tx controller unit number (for -a) ... (default unit %d)\n", TxUDEF);
+ fprintf(stderr,"\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * main
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char *argv[])
+{
+ char devicename[80];
+ char headerbuf[256];
+
+ int n;
+ int c;
+ char *b;
+
+ char *outfile = DECODE_FILE_NAME;
+ char *binfile = BIN_FILE_NAME;
+ int outfileset = 0;
+ time_t tm;
+
+ i4b_trace_hdr_t *ithp = NULL;
+ int l;
+
+ b = &buf[sizeof(i4b_trace_hdr_t)];
+
+ while( (c = getopt(argc, argv, "abdf:hiln:op:u:xBPR:T:")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ analyze = 1;
+ break;
+
+ case 'b':
+ enable_trace |= (TRACE_B_RX | TRACE_B_TX);
+ break;
+
+ case 'd':
+ enable_trace &= (~(TRACE_D_TX | TRACE_D_RX));
+ break;
+
+ case 'o':
+ outflag = 0;
+ break;
+
+ case 'f':
+ outfile = optarg;
+ outfileset = 1;
+ break;
+
+ case 'h':
+ header = 0;
+ break;
+
+ case 'i':
+ enable_trace |= TRACE_I;
+ info = 1;
+ break;
+
+ case 'l':
+ print_q921 = 0;
+ break;
+
+ case 'p':
+ binfile = optarg;
+ bpopt = 1;
+ break;
+
+ case 'u':
+ unit = atoi(optarg);
+ if(unit < 0 || unit >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'x':
+ xflag = 1;
+ break;
+
+ case 'B':
+ Bopt = 1;
+ break;
+
+ case 'P':
+ Popt = 1;
+ break;
+
+ case 'R':
+ Rx = atoi(optarg);
+ if(Rx < 0 || Rx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'T':
+ Tx = atoi(optarg);
+ if(Tx < 0 || Tx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(enable_trace == 0)
+ usage();
+
+ if(Bopt && Popt)
+ usage();
+
+ atexit(exit_hdl);
+
+ if(Bopt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ if((BP = fopen(BPfilename, "r")) != NULL)
+ {
+ char buffer[1024];
+ fclose(BP);
+ sprintf(buffer, "%s%s", BPfilename, DECODE_FILE_NAME_BAK);
+ rename(BPfilename, buffer);
+ }
+ if((BP = fopen(BPfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Popt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ if((BP = fopen(BPfilename, "r")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+ else
+ {
+ sprintf(devicename, "%s%d", I4BTRC_DEVICE, unit);
+
+ if((f = open(devicename, O_RDWR)) < 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening trace device [%s]", devicename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(outflag)
+ {
+ if(outfileset == 0)
+ sprintf(outfilename, "%s%d", DECODE_FILE_NAME, unit);
+ else
+ strcpy(outfilename, outfile);
+
+
+ if((Fout = fopen(outfilename, "r")) != NULL)
+ {
+ char buffer[1024];
+ fclose(Fout);
+ sprintf(buffer, "%s%s", outfilename, DECODE_FILE_NAME_BAK);
+ rename(outfilename, buffer);
+ }
+
+ if((Fout = fopen(outfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", outfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", outfile);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if((setvbuf(stdout, (char *)NULL, _IOLBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting stdout to line-buffered");
+ perror(buffer);
+ exit(1);
+ }
+
+ if(!Popt)
+ {
+ if((switch_driver(enable_trace, Rx, Tx)) == -1)
+ exit(1);
+ else
+ traceon = 1;
+ }
+
+ signal(SIGHUP, SIG_IGN); /* ignore hangup signal */
+ signal(SIGUSR1, reopenfiles); /* rotate logfile(s) */
+
+ time(&tm);
+
+ if(analyze)
+ {
+ sprintf(headerbuf, "\n==== isdnanalyze controller rx #%d - tx #%d ==== started %s",
+ Rx, Tx, ctime(&tm));
+ }
+ else
+ {
+ sprintf(headerbuf, "\n=========== isdntrace controller #%d =========== started %s",
+ unit, ctime(&tm));
+ }
+
+ printf("%s", headerbuf);
+
+ if(outflag)
+ fprintf(Fout, "%s", headerbuf);
+
+ for (;;)
+ {
+ if(Popt == 0)
+ {
+ n = read(f, buf, BSIZE);
+
+ if(Bopt)
+ {
+ if((fwrite(buf, 1, n, BP)) != n)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error writing file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ n -= sizeof(i4b_trace_hdr_t);
+ }
+ else
+ {
+ if((fread(buf, 1, sizeof(i4b_trace_hdr_t), BP)) != sizeof(i4b_trace_hdr_t))
+ {
+ if(feof(BP))
+ {
+ printf("\nEnd of playback input file reached.\n");
+ exit(0);
+ }
+ else
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading hdr from file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ ithp = (i4b_trace_hdr_t *)buf;
+ l = ithp->length - sizeof(i4b_trace_hdr_t);
+
+ if((n = fread(buf+sizeof(i4b_trace_hdr_t), 1, l , BP)) != l)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading data from file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ }
+
+ if(n > 0)
+ {
+ dumpbuf(n, b, (i4b_trace_hdr_t *)buf);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * format header into static buffer, return buffer address
+ *---------------------------------------------------------------------------*/
+char *
+fmt_hdr(i4b_trace_hdr_t *hdr, int frm_len)
+{
+ struct tm *s;
+ static char hbuf[256];
+ int i = 0;
+
+ s = localtime((time_t *)&(hdr->time.tv_sec));
+
+ if(hdr->type == TRC_CH_I) /* Layer 1 INFO's */
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d ---------------- time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec);
+ }
+ else
+ {
+ if(hdr->trunc > 0)
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d (%d) ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len,
+ hdr->trunc);
+ }
+ else
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len);
+ }
+ }
+
+ for(i=strlen(hbuf); i <= NCOLS;)
+ hbuf[i++] = '-';
+
+ hbuf[i++] = '\n';
+ hbuf[i] = '\0';
+
+ return(hbuf);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode protocol and output to file(s)
+ *---------------------------------------------------------------------------*/
+static void
+dumpbuf(int n, unsigned char *buf, i4b_trace_hdr_t *hdr)
+{
+ static char l1buf[128];
+ static unsigned char l2buf[32000];
+ static unsigned char l3buf[32000];
+ int cnt;
+ int nsave = n;
+ char *pbuf;
+ int i, j;
+
+ l1buf[0] = '\0';
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+
+ switch(hdr->type)
+ {
+ case TRC_CH_I: /* Layer 1 INFO's */
+ if(enable_trace & TRACE_I)
+ layer1(l1buf, buf);
+ break;
+
+ case TRC_CH_D: /* D-channel data */
+ cnt = layer2(l2buf, buf, hdr->dir, print_q921);
+
+ if(print_q921 == 0)
+ l2buf[0] = '\0';
+
+ n -= cnt;
+ buf += cnt;
+
+ if(n)
+ {
+ if((*buf != 0x08) && (xflag == 0))
+ {
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+ break;
+ }
+ layer3(l3buf, n, cnt, buf);
+ }
+ break;
+
+ default: /* B-channel data */
+
+ pbuf = &l2buf[0];
+
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"B%d:%.3x ", hdr->type, i);
+
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ break;
+ }
+
+ if(header && ((l1buf[0] != '\0' || l2buf[0] != '\0') || (l3buf[0] != 0)))
+ {
+ char *p;
+ p = fmt_hdr(hdr, nsave);
+ printf("%s", p);
+ if(outflag)
+ fprintf(Fout, "%s", p);
+ }
+
+ if(l1buf[0] != '\0')
+ {
+ printf("%s", l1buf);
+ if(outflag)
+ fprintf(Fout, "%s", l1buf);
+ }
+
+ if(l2buf[0] != '\0')
+ {
+ printf("%s", l2buf);
+ if(outflag)
+ fprintf(Fout, "%s", l2buf);
+ }
+
+ if(l3buf[0] != '\0')
+ {
+ printf("%s", l3buf);
+ if(outflag)
+ fprintf(Fout, "%s", l3buf);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * exit handler function to be called at program exit
+ *---------------------------------------------------------------------------*/
+void
+exit_hdl()
+{
+ if(traceon)
+ switch_driver(TRACE_OFF, Rx, Tx);
+}
+
+/*---------------------------------------------------------------------------*
+ * switch driver debugging output on/off
+ *---------------------------------------------------------------------------*/
+static int
+switch_driver(int value, int rx, int tx)
+{
+ char buffer[80];
+ int v = value;
+
+ if(analyze == 0)
+ {
+ if(ioctl(f, I4B_TRC_SET, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SET, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ if(value == TRACE_OFF)
+ {
+ if(ioctl(f, I4B_TRC_RESETA, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_RESETA - ");
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ i4b_trace_setupa_t tsa;
+
+ tsa.rxunit = rx;
+ tsa.rxflags = value;
+ tsa.txunit = tx;
+ tsa.txflags = value;
+
+ if(ioctl(f, I4B_TRC_SETA, &tsa) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SETA, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * reopen files to support rotating logfile(s) on SIGUSR1
+ *
+ * based on an idea from Ripley (ripley@nostromo.in-berlin.de)
+ *
+ * close file and reopen it for append. this will be a nop
+ * if the previously opened file hasn't moved but will open
+ * a new one otherwise, thus enabling a rotation...
+ *
+ *---------------------------------------------------------------------------*/
+static void
+reopenfiles(int dummy)
+{
+ if(outflag)
+ {
+ fclose(Fout);
+
+ if((Fout = fopen(outfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", outfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", outfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Bopt)
+ {
+
+ fclose(BP);
+
+ if((BP = fopen(BPfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * decode extension bit
+ *---------------------------------------------------------------------------*/
+void
+extension(int layer, char *buffer, int cnt, unsigned char value, unsigned char mask)
+{
+ sprintline(layer, buffer, cnt, value, mask, "Extension Bit = %c (%s)",
+ (value & mask) ? '1' : '0',
+ (value & mask) ? "no extension, final octet" : "with extension, octet follows");
+}
+
+/*---------------------------------------------------------------------------*
+ * print bits as 0/1 available for mask
+ *---------------------------------------------------------------------------*/
+static char *
+print_bits(unsigned char val, unsigned char mask)
+{
+ static char buffer[10];
+ int i = 0;
+ int length = 8;
+
+ while(length--)
+ {
+ if(mask & 0x80)
+ {
+ if(val & 0x80)
+ buffer[i++] = '1';
+ else
+ buffer[i++] = '0';
+ }
+ else
+ {
+ buffer[i++] = '-';
+ }
+ val = val << 1;
+ mask = mask << 1;
+ }
+ buffer[i] = '\0';
+ return(buffer);
+}
+
+/*---------------------------------------------------------------------------*
+ * print one decoded output line
+ *---------------------------------------------------------------------------*/
+void
+sprintline(int layer, char *buffer, int oct_count, int oct_val,
+ int oct_mask, const char *fmt, ...)
+{
+ char lbuffer[256];
+ static int lastcount = -1;
+ char *ptr;
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(oct_count != lastcount)
+ {
+ lastcount = oct_count;
+
+ sprintf(lbuffer, "L%d %2d %02X %s ",
+ layer,
+ oct_count,
+ oct_val,
+ print_bits(oct_val, oct_mask));
+ }
+ else
+ {
+ sprintf(lbuffer, " %s ",
+ print_bits(oct_val, oct_mask));
+ }
+
+ vsprintf(lbuffer+strlen(lbuffer), fmt, ap);
+
+ va_end(ap);
+
+ sprintf(lbuffer+strlen(lbuffer), "\n");
+
+ if((ptr = rindex(lbuffer, '(')) != NULL)
+ {
+ char *s = lbuffer;
+ char *b = buffer;
+ int len = strlen(lbuffer);
+ int i;
+
+ for(s = lbuffer; s < ptr; *b++ = *s++)
+ ;
+ for(i = 0;(i+len) <= NCOLS; *b++ = ' ', i++)
+ ;
+ for(; *s; *b++ = *s++)
+ ;
+ *b = '\0';
+ }
+ else
+ {
+ strcpy(buffer, lbuffer);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/pcause.c b/usr.sbin/i4b/isdndecode/pcause.c
new file mode 100644
index 0000000..610a960
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/pcause.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcause.c - printing cause values
+ * --------------------------------
+ *
+ * $Id: pcause.c,v 1.5 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:51:20 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.h"
+#include "pcause.h"
+
+char *
+print_cause_q850(unsigned char code)
+{
+ static char error_message[120];
+ char *e;
+
+ switch(code)
+ {
+ case CAUSE_Q850_SHUTDN:
+ e = "normal D-channel shutdown";
+ break;
+
+ case CAUSE_Q850_NUNALLC:
+ e = "Unallocated (unassigned) number";
+ break;
+
+ case CAUSE_Q850_NRTTN:
+ e = "No route to specified transit network";
+ break;
+
+ case CAUSE_Q850_NRTDST:
+ e = "No route to destination";
+ break;
+
+ case CAUSE_Q850_SSINFTN:
+ e = "Send special information tone";
+ break;
+
+ case CAUSE_Q850_MDIALTP:
+ e = "Misdialled trunk prefix";
+ break;
+
+ case CAUSE_Q850_CHUNACC:
+ e = "Channel unacceptable";
+ break;
+
+ case CAUSE_Q850_CALLAWD:
+ e = "Call awarded and being delivered in an established channel";
+ break;
+
+ case CAUSE_Q850_PREEMPT:
+ e = "Preemption";
+ break;
+
+ case CAUSE_Q850_PREECRR:
+ e = "Preemption - circuit reserved for reuse";
+ break;
+
+ case CAUSE_Q850_NCCLR:
+ e = "Normal call clearing";
+ break;
+
+ case CAUSE_Q850_USRBSY:
+ e = "User busy";
+ break;
+
+ case CAUSE_Q850_NOUSRRSP:
+ e = "No user responding";
+ break;
+
+ case CAUSE_Q850_NOANSWR:
+ e = "No answer from user (user alerted)";
+ break;
+
+ case CAUSE_Q850_SUBSABS:
+ e = "Subscriber absent";
+ break;
+
+ case CAUSE_Q850_CALLREJ:
+ e = "Call rejected";
+ break;
+
+ case CAUSE_Q850_NUCHNG:
+ e = "Number changed";
+ break;
+
+ case CAUSE_Q850_NONSELUC:
+ e = "Non-selected user clearing";
+ break;
+
+ case CAUSE_Q850_DSTOOORDR:
+ e = "Destination out of order";
+ break;
+
+ case CAUSE_Q850_INVNUFMT:
+ e = "Invalid number format";
+ break;
+
+ case CAUSE_Q850_FACREJ:
+ e = "Facility rejected";
+ break;
+
+ case CAUSE_Q850_STENQRSP:
+ e = "Response to STATUS ENQUIRY";
+ break;
+
+ case CAUSE_Q850_NORMUNSP:
+ e = "Normal, unspecified";
+ break;
+
+ case CAUSE_Q850_NOCAVAIL:
+ e = "No circuit / channel available";
+ break;
+
+ case CAUSE_Q850_NETOOORDR:
+ e = "Network out of order";
+ break;
+
+ case CAUSE_Q850_PFMCDOOSERV:
+ e = "Permanent frame mode connection out of service";
+ break;
+
+ case CAUSE_Q850_PFMCOPER:
+ e = "Permanent frame mode connection operational";
+ break;
+
+ case CAUSE_Q850_TMPFAIL:
+ e = "Temporary failure";
+ break;
+
+ case CAUSE_Q850_SWEQCONG:
+ e = "Switching equipment congestion";
+ break;
+
+ case CAUSE_Q850_ACCINFDIS:
+ e = "Access information discarded";
+ break;
+
+ case CAUSE_Q850_REQCNOTAV:
+ e = "Requested circuit/channel not available";
+ break;
+
+ case CAUSE_Q850_PRECALBLK:
+ e = "Precedence call blocked";
+ break;
+
+ case CAUSE_Q850_RESUNAVAIL:
+ e = "Resources unavailable, unspecified";
+ break;
+
+ case CAUSE_Q850_QOSUNAVAIL:
+ e = "Quality of service unavailable";
+ break;
+
+ case CAUSE_Q850_REQSERVNS:
+ e = "Requested facility not subscribed";
+ break;
+
+ case CAUSE_Q850_OCBARRCUG:
+ e = "Outgoing calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_ICBARRCUG:
+ e = "Incoming calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_BCAPNAUTH:
+ e = "Bearer capability not authorized";
+ break;
+
+ case CAUSE_Q850_BCAPNAVAIL:
+ e = "Bearer capability not presently available";
+ break;
+
+ case CAUSE_Q850_INCSTOACISC:
+ e = "Inconsistenciy in designated outg. access info and subscriber class";
+ break;
+
+ case CAUSE_Q850_SOONOTAVAIL:
+ e = "Service or option not available, unspecified";
+ break;
+
+ case CAUSE_Q850_BCAPNOTIMPL:
+ e = "Bearer capability not implemented";
+ break;
+
+ case CAUSE_Q850_CHTYPNIMPL:
+ e = "Channel type not implemented";
+ break;
+
+ case CAUSE_Q850_REQFACNIMPL:
+ e = "Requested facility not implemented";
+ break;
+
+ case CAUSE_Q850_ORDINBCAVL:
+ e = "Only restricted digital information bearer capability is available";
+ break;
+
+ case CAUSE_Q850_SOONOTIMPL:
+ e = "Service or option not implemented, unspecified";
+ break;
+
+ case CAUSE_Q850_INVCLRFVAL:
+ e = "Invalid call reference value";
+ break;
+
+ case CAUSE_Q850_IDCHDNOEX:
+ e = "Identified channel does not exist";
+ break;
+
+ case CAUSE_Q850_SUSCAEXIN:
+ e = "A suspended call exists, but this call identity does not";
+ break;
+
+ case CAUSE_Q850_CLIDINUSE:
+ e = "Call identity in use";
+ break;
+
+ case CAUSE_Q850_NOCLSUSP:
+ e = "No call suspended";
+ break;
+
+ case CAUSE_Q850_CLIDCLRD:
+ e = "Call having the requested call identity has been cleared";
+ break;
+
+ case CAUSE_Q850_UNOTMEMCUG:
+ e = "User not member of CUG";
+ break;
+
+ case CAUSE_Q850_INCDEST:
+ e = "Incompatible destination";
+ break;
+
+ case CAUSE_Q850_NONEXCUG:
+ e = "Non-existent CUG";
+ break;
+
+ case CAUSE_Q850_INVNTWSEL:
+ e = "Invalid transit network selection";
+ break;
+
+ case CAUSE_Q850_INVMSG:
+ e = "Invalid message, unspecified";
+ break;
+
+ case CAUSE_Q850_MIEMISS:
+ e = "Mandatory information element is missing";
+ break;
+
+ case CAUSE_Q850_MSGTNI:
+ e = "Message type non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_MSGNCMPT:
+ e = "Msg incompatible with call state/message type non-existent/not implemented";
+ break;
+
+ case CAUSE_Q850_IENENI:
+ e = "Information element/parameter non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_INVIEC:
+ e = "Invalid information element contents";
+ break;
+
+ case CAUSE_Q850_MSGNCWCS:
+ e = "Message not compatible with call state";
+ break;
+
+ case CAUSE_Q850_RECOTIMEXP:
+ e = "Recovery on timer expiry";
+ break;
+
+ case CAUSE_Q850_PARMNENIPO:
+ e = "Parameter non-existent or not implemented, passed on";
+ break;
+
+ case CAUSE_Q850_MSGUNRDPRM:
+ e = "Message with unrecognized parameter, discarded";
+ break;
+
+ case CAUSE_Q850_PROTERR:
+ e = "Protocol error, unspecified";
+ break;
+
+ case CAUSE_Q850_INTWRKU:
+ e = "Interworking, unspecified";
+ break;
+
+ default:
+ e = "ERROR, unknown cause value!";
+ break;
+ }
+
+ sprintf(error_message, "%d: %s (Q.850)", code, e);
+ return(error_message);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/pcause.h b/usr.sbin/i4b/isdndecode/pcause.h
new file mode 100644
index 0000000..2c1c7f4
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/pcause.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcause.h - Q.850 causes definitions
+ * -----------------------------------
+ *
+ * $Id: pcause.h,v 1.4 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:51:32 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+char *print_cause_q850(unsigned char code);
+
+/* Q.850 causes */
+
+#define CAUSE_Q850_SHUTDN 0x00 /* normal D-channel shutdown */
+#define CAUSE_Q850_NUNALLC 0x01 /* Unallocated (unassigned) number */
+#define CAUSE_Q850_NRTTN 0x02 /* No route to specified transit network */
+#define CAUSE_Q850_NRTDST 0x03 /* No route to destination */
+#define CAUSE_Q850_SSINFTN 0x04 /* Send special information tone */
+#define CAUSE_Q850_MDIALTP 0x05 /* Misdialled trunk prefix */
+#define CAUSE_Q850_CHUNACC 0x06 /* Channel unacceptable */
+#define CAUSE_Q850_CALLAWD 0x07 /* Call awarded and being delivered in an established channel */
+#define CAUSE_Q850_PREEMPT 0x08 /* Preemption */
+#define CAUSE_Q850_PREECRR 0x09 /* Preemption - circuit reserved for reuse */
+#define CAUSE_Q850_NCCLR 0x10 /* Normal call clearing */
+#define CAUSE_Q850_USRBSY 0x11 /* User busy */
+#define CAUSE_Q850_NOUSRRSP 0x12 /* No user responding */
+#define CAUSE_Q850_NOANSWR 0x13 /* No answer from user (user alerted) */
+#define CAUSE_Q850_SUBSABS 0x14 /* Subscriber absent */
+#define CAUSE_Q850_CALLREJ 0x15 /* Call rejected */
+#define CAUSE_Q850_NUCHNG 0x16 /* Number changed */
+#define CAUSE_Q850_NONSELUC 0x1A /* Non-selected user clearing */
+#define CAUSE_Q850_DSTOOORDR 0x1B /* Destination out of order */
+#define CAUSE_Q850_INVNUFMT 0x1C /* Invalid number format */
+#define CAUSE_Q850_FACREJ 0x1D /* Facility rejected */
+#define CAUSE_Q850_STENQRSP 0x1E /* Response to STATUS ENQUIRY */
+#define CAUSE_Q850_NORMUNSP 0x1F /* Normal, unspecified */
+#define CAUSE_Q850_NOCAVAIL 0x22 /* No circuit / channel available */
+#define CAUSE_Q850_NETOOORDR 0x26 /* Network out of order */
+#define CAUSE_Q850_PFMCDOOSERV 0x27 /* Permanent frame mode connection out of service */
+#define CAUSE_Q850_PFMCOPER 0x28 /* Permanent frame mode connection operational */
+#define CAUSE_Q850_TMPFAIL 0x29 /* Temporary failure */
+#define CAUSE_Q850_SWEQCONG 0x2A /* Switching equipment congestion */
+#define CAUSE_Q850_ACCINFDIS 0x2B /* Access information discarded */
+#define CAUSE_Q850_REQCNOTAV 0x2C /* Requested circuit/channel not available */
+#define CAUSE_Q850_PRECALBLK 0x2E /* Precedence call blocked */
+#define CAUSE_Q850_RESUNAVAIL 0x2F /* Resources unavailable, unspecified */
+#define CAUSE_Q850_QOSUNAVAIL 0x31 /* Quality of service unavailable */
+#define CAUSE_Q850_REQSERVNS 0x32 /* Requested facility not subscribed */
+#define CAUSE_Q850_OCBARRCUG 0x35 /* Outgoing calls barred within CUG */
+#define CAUSE_Q850_ICBARRCUG 0x36 /* Incoming calls barred within CUG */
+#define CAUSE_Q850_BCAPNAUTH 0x39 /* Bearer capability not authorized */
+#define CAUSE_Q850_BCAPNAVAIL 0x3A /* Bearer capability not presently available */
+#define CAUSE_Q850_INCSTOACISC 0x3E /* Inconsistenciy in designated outgoing access information and subscriber class */
+#define CAUSE_Q850_SOONOTAVAIL 0x3F /* Service or option not available, unspecified */
+#define CAUSE_Q850_BCAPNOTIMPL 0x41 /* Bearer capability not implemented */
+#define CAUSE_Q850_CHTYPNIMPL 0x42 /* Channel type not implemented */
+#define CAUSE_Q850_REQFACNIMPL 0x45 /* Requested facility not implemented */
+#define CAUSE_Q850_ORDINBCAVL 0x46 /* Only restricted digital information bearer capability is available */
+#define CAUSE_Q850_SOONOTIMPL 0x4F /* Service or option not implemented, unspecified */
+#define CAUSE_Q850_INVCLRFVAL 0x51 /* Invalid call reference value */
+#define CAUSE_Q850_IDCHDNOEX 0x52 /* Identified channel does not exist */
+#define CAUSE_Q850_SUSCAEXIN 0x53 /* A suspended call exists, but this call identity does not */
+#define CAUSE_Q850_CLIDINUSE 0x54 /* Call identity in use */
+#define CAUSE_Q850_NOCLSUSP 0x55 /* No call suspended */
+#define CAUSE_Q850_CLIDCLRD 0x56 /* Call having the requested call identity has been cleared */
+#define CAUSE_Q850_UNOTMEMCUG 0x57 /* User not member of CUG */
+#define CAUSE_Q850_INCDEST 0x58 /* Incompatible destination */
+#define CAUSE_Q850_NONEXCUG 0x5A /* Non-existent CUG */
+#define CAUSE_Q850_INVNTWSEL 0x5B /* Invalid transit network selection */
+#define CAUSE_Q850_INVMSG 0x5F /* Invalid message, unspecified */
+#define CAUSE_Q850_MIEMISS 0x60 /* Mandatory information element is missing */
+#define CAUSE_Q850_MSGTNI 0x61 /* Message type non-existent or not implemented */
+#define CAUSE_Q850_MSGNCMPT 0x62 /* Message not compatible with call state or message type non-existent or not implemented */
+#define CAUSE_Q850_IENENI 0x63 /* Information element/parameter non-existent or not implemented */
+#define CAUSE_Q850_INVIEC 0x64 /* Invalid information element contents */
+#define CAUSE_Q850_MSGNCWCS 0x65 /* Message not compatible with call state */
+#define CAUSE_Q850_RECOTIMEXP 0x66 /* Recovery on timer expiry */
+#define CAUSE_Q850_PARMNENIPO 0x67 /* Parameter non-existent or not implemented, passed on */
+#define CAUSE_Q850_MSGUNRDPRM 0x6E /* Message with unrecognized parameter, discarded */
+#define CAUSE_Q850_PROTERR 0x6F /* Protocol error, unspecified */
+#define CAUSE_Q850_INTWRKU 0x7F /* Interworking, unspecified */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnmonitor/Makefile b/usr.sbin/i4b/isdnmonitor/Makefile
new file mode 100644
index 0000000..2eb82d4
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+PROG= isdnmonitor
+MAN= isdnmonitor.8
+SRCS= main.c curses.c
+
+# compile debug support
+COPTS+= -DDEBUG
+
+# avoid wacky merging of string constants from
+# source code with compile-time timestamp
+COPTS+= -fno-merge-constants
+
+DPADD= ${LIBCURSES}
+LDADD= -lcurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdnmonitor/curses.c b/usr.sbin/i4b/isdnmonitor/curses.c
new file mode 100644
index 0000000..1e7eec1
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/curses.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - curses fullscreen output
+ * -------------------------------------
+ *
+ * $Id: curses.c,v 1.10 1999/12/13 21:25:25 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:51:47 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "monprivate.h"
+
+#ifndef WIN32
+
+static void display_bell(void);
+static void display_chans(void);
+
+/*---------------------------------------------------------------------------*
+ * program exit
+ *---------------------------------------------------------------------------*/
+void
+do_exit(int exitval)
+{
+ if(curses_ready)
+ endwin();
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_screen(void)
+{
+ char buffer[512];
+ int uheight, lheight;
+ int i, j;
+
+ initscr(); /* curses init */
+
+ if((COLS < 80) || (LINES < 24))
+ {
+ endwin();
+ fprintf(stderr, "ERROR, minimal screensize must be 80x24, is %dx%d, terminating!",COLS, LINES);
+ exit(1);
+ }
+
+ noecho();
+ raw();
+
+ uheight = nctrl * 2; /* cards * b-channels */
+ lheight = LINES - uheight - 6 + 1; /* rest of display */
+
+ if((upper_w = newwin(uheight, COLS, UPPER_B, 0)) == NULL)
+ {
+ endwin();
+ fprintf(stderr, "ERROR, curses init upper window, terminating!");
+ exit(1);
+ }
+
+ if((mid_w = newwin(1, COLS, UPPER_B+uheight+1, 0)) == NULL)
+ {
+ endwin();
+ fprintf(stderr, "ERROR, curses init mid window, terminating!");
+ exit(1);
+ }
+
+ if((lower_w = newwin(lheight, COLS, UPPER_B+uheight+3, 0)) == NULL)
+ {
+ endwin();
+ fprintf(stderr, "ERROR, curses init lower window, LINES = %d, lheight = %d, uheight = %d, terminating!", LINES, lheight, uheight);
+ exit(1);
+ }
+
+ scrollok(lower_w, 1);
+
+ sprintf(buffer, "----- isdn controller channel state ------------- isdnmonitor %02d.%02d.%d -", VERSION, REL, STEP);
+
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(0, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ move(1, 0);
+ /* 01234567890123456789012345678901234567890123456789012345678901234567890123456789 */
+ addstr("c tei b remote iface dir outbytes obps inbytes ibps units");
+
+ if(hostname)
+ sprintf(buffer, "----- isdn userland interface state ------------- %s:%d -", hostname, portno);
+ else
+ sprintf(buffer, "----- isdn userland interface state ------------- %s -", sockpath);
+
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+2, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ sprintf(buffer, "----- isdnd logfile display --------------------------------------------------");
+ while(strlen(buffer) < COLS)
+ strcat(buffer, "-");
+
+ move(uheight+4, 0);
+ standout();
+ addstr(buffer);
+ standend();
+
+ refresh();
+
+ for(i=0, j=0; i <= nctrl; i++, j+=2)
+ {
+ mvwprintw(upper_w, j, H_CNTL, "%d --- 1 ", i); /*TEI*/
+ mvwprintw(upper_w, j+1, H_CNTL, " L12 2 ");
+ }
+ wrefresh(upper_w);
+
+#ifdef NOTDEF
+ for(i=0, j=0; i < nentries; i++) /* walk thru all entries */
+ {
+ p = &cfg_entry_tab[i]; /* get ptr to enry */
+
+ mvwprintw(mid_w, 0, j, "%s%d ", bdrivername(p->usrdevicename), p->usrdeviceunit);
+
+ p->fs_position = j;
+
+ j += ((strlen(bdrivername(p->usrdevicename)) + (p->usrdeviceunit > 9 ? 2 : 1) + 1));
+ }
+#else
+ mvwprintw(mid_w, 0, 0, "%s", devbuf);
+#endif
+ wrefresh(mid_w);
+
+ wmove(lower_w, 0, 0);
+ wrefresh(lower_w);
+
+ curses_ready = 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * display the charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_charge(int pos, int charge)
+{
+ mvwprintw(upper_w, pos, H_UNITS, "%d", charge);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display the calculated charge in units
+ *---------------------------------------------------------------------------*/
+void
+display_ccharge(int pos, int units)
+{
+ mvwprintw(upper_w, pos, H_UNITS, "(%d)", units);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display accounting information
+ *---------------------------------------------------------------------------*/
+void
+display_acct(int pos, int obyte, int obps, int ibyte, int ibps)
+{
+ mvwprintw(upper_w, pos, H_OUT, "%-10d", obyte);
+ mvwprintw(upper_w, pos, H_OUTBPS, "%-4d", obps);
+ mvwprintw(upper_w, pos, H_IN, "%-10d", ibyte);
+ mvwprintw(upper_w, pos, H_INBPS, "%-4d", ibps);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * erase line at disconnect time
+ *---------------------------------------------------------------------------*/
+void
+display_disconnect(int pos)
+{
+ wmove(upper_w, pos, H_TELN);
+ wclrtoeol(upper_w);
+ wrefresh(upper_w);
+
+ if(do_bell)
+ display_bell();
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_updown(int pos, int updown, char *device)
+{
+ if(updown)
+ wstandend(mid_w);
+ else
+ wstandout(mid_w);
+
+ mvwprintw(mid_w, 0, pos, "%s ", device);
+
+ wstandend(mid_w);
+ wrefresh(mid_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display interface up/down information
+ *---------------------------------------------------------------------------*/
+void
+display_l12stat(int controller, int layer, int state)
+{
+ if(controller > nctrl)
+ return;
+
+ if(!(layer == 1 || layer == 2))
+ return;
+
+ if(state)
+ wstandout(upper_w);
+ else
+ wstandend(upper_w);
+
+ if(layer == 1)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+
+ if(!state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ }
+ else if(layer == 2)
+ {
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+2, "2");
+ if(state)
+ mvwprintw(upper_w, (controller*2)+1, H_TEI+1, "1");
+ }
+
+ wstandend(upper_w);
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display TEI
+ *---------------------------------------------------------------------------*/
+void
+display_tei(int controller, int tei)
+{
+ if(controller > nctrl)
+ return;
+
+ if(tei == -1)
+ mvwprintw(upper_w, controller*2, H_TEI, "---");
+ else
+ mvwprintw(upper_w, controller*2, H_TEI, "%3d", tei);
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display bell :-)
+ *---------------------------------------------------------------------------*/
+static void
+display_bell(void)
+{
+ static char bell[1] = { 0x07 };
+ write(STDOUT_FILENO, &bell[0], 1);
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "1 - (D)isplay refresh",
+ "2 - (H)angup (choose a channel)",
+ "3 - (R)eread config file",
+ "4 - (Q)uit the program",
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+ fd_set set;
+ struct timeval timeout;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ {
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(menu_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ goto mexit;
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case ' ':
+ case '\t': /* hilite next option */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case ('0'+WREFRESH+1): /* display refresh */
+ case 'D':
+ case 'd':
+ wrefresh(curscr);
+ goto mexit;
+
+ case ('0'+WQUIT+1): /* quit program */
+ case 'Q':
+ case 'q':
+ do_exit(0);
+ goto mexit;
+
+
+ case ('0'+WHANGUP+1): /* hangup connection */
+ case 'H':
+ case 'h':
+ display_chans();
+ goto mexit;
+
+ case ('0'+WREREAD+1): /* reread config file */
+ case 'R':
+ case 'r':
+ reread();
+ goto mexit;
+
+ case '\n':
+ case '\r': /* exec highlighted option */
+ switch(mpos)
+ {
+ case WREFRESH:
+ wrefresh(curscr);
+ break;
+
+ case WQUIT:
+ do_exit(0);
+ break;
+
+ case WHANGUP:
+ display_chans();
+ break;
+
+ case WREREAD:
+ reread();
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ /* delete the menu window */
+
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(lower_w);
+ wrefresh(lower_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display connect information
+ *---------------------------------------------------------------------------*/
+void
+display_connect(int pos, int dir, char *name, char *remtel, char *dev)
+{
+ char buffer[256];
+
+ /* remote telephone number */
+
+ sprintf(buffer, "%s/%s", name, remtel);
+
+ buffer[H_IFN - H_TELN - 1] = '\0';
+
+ mvwprintw(upper_w, pos, H_TELN, "%s", buffer);
+
+ /* interface */
+
+ mvwprintw(upper_w, pos, H_IFN, "%s ", dev);
+
+ mvwprintw(upper_w, pos, H_IO, dir ? "out" : "in");
+
+ mvwprintw(upper_w, pos, H_OUT, "-");
+ mvwprintw(upper_w, pos, H_OUTBPS, "-");
+ mvwprintw(upper_w, pos, H_IN, "-");
+ mvwprintw(upper_w, pos, H_INBPS, "-");
+
+ if(do_bell)
+ display_bell();
+
+ wrefresh(upper_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * display channel information for shutdown
+ *---------------------------------------------------------------------------*/
+static void
+display_chans(void)
+{
+ char buffer[80];
+ int i;
+ int cnt = 0;
+ WINDOW *chan_w;
+ int nlines, ncols, pos_x, pos_y;
+ fd_set set;
+ struct timeval timeout;
+
+ /* need this later to close the connection */
+ struct ctlr_chan {
+ int cntl;
+ int chn;
+ } *cc = NULL;
+
+ for(i = 0; i < nctrl; i++)
+ {
+ if(remstate[i].ch1state)
+ cnt++;
+ if(remstate[i].ch2state)
+ cnt++;
+ }
+
+ if(cnt > 0)
+ {
+ if ((cc = (struct ctlr_chan *)malloc (cnt *
+ sizeof (struct ctlr_chan))) == NULL)
+ {
+ return;
+ }
+ nlines = cnt + 4;
+ ncols = 60;
+ }
+ else
+ {
+ nlines = 5;
+ ncols = 22;
+ }
+
+ pos_y = WMENU_POSLN + 4;
+ pos_x = WMENU_POSCO + 10;
+
+ /* create a new window in the lower screen area */
+
+ if((chan_w = newwin(nlines, ncols, pos_y, pos_x )) == NULL)
+ {
+ if (cnt > 0)
+ free(cc);
+ return;
+ }
+
+ /* create a border around the window */
+
+ box(chan_w, '|', '-');
+
+ /* add a title */
+
+ wstandout(chan_w);
+ mvwaddstr(chan_w, 0, (ncols / 2) - (strlen("Channels") / 2), "Channels");
+ wstandend(chan_w);
+
+ /* no active channels */
+ if (cnt == 0)
+ {
+ mvwaddstr(chan_w, 2, 2, "No active channels");
+ wrefresh(chan_w);
+ sleep(1);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+ return;
+ }
+
+ nlines = 2;
+ ncols = 1;
+
+ for (i = 0; i < nctrl; i++)
+ {
+ if(remstate[i].ch1state)
+ {
+ sprintf(buffer, "%d - Controller %d channel %s", ncols, i, "B1");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B1;
+ nlines++;
+ ncols++;
+ }
+ if(remstate[i].ch2state)
+ {
+ sprintf(buffer, "%d - Controller %d channel %s", ncols, i, "B2");
+ mvwaddstr(chan_w, nlines, 2, buffer);
+ cc[ncols - 1].cntl = i;
+ cc[ncols - 1].chn = CHAN_B2;
+ nlines++;
+ ncols++;
+ }
+ }
+
+ for(;;)
+ {
+ wrefresh(chan_w);
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = WMTIMEOUT;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, exit menu*/
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ break;
+
+ ncols = wgetch(chan_w);
+
+ if (!(isdigit(ncols)))
+ {
+ display_bell();
+ continue;
+ }
+
+ nlines = ncols - '0';
+
+ if ((nlines == 0) || (nlines > cnt))
+ {
+ display_bell();
+ continue;
+ }
+
+ hangup(cc[nlines-1].cntl, cc[nlines-1].chn);
+ break;
+ }
+
+ free(cc);
+
+ /* delete the channels window */
+
+ delwin(chan_w);
+}
+
+#endif /* !WIN32*/
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnmonitor/isdnmonitor.8 b/usr.sbin/i4b/isdnmonitor/isdnmonitor.8
new file mode 100644
index 0000000..5e6d5c3
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/isdnmonitor.8
@@ -0,0 +1,174 @@
+.\"
+.\" Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdnmonitor.8,v 1.8 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:04:25 1999]
+.\"
+.Dd September 25, 1999
+.Dt ISDNMONITOR 8
+.Os
+.Sh NAME
+.Nm isdnmonitor
+.Nd isdn4bsd / isdnd remote monitoring tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl d Ar debuglevel
+.Op Fl f Ar filename
+.Op Fl h Ar hostspec
+.Op Fl l Ar pathname
+.Op Fl p Ar portspec
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to remotely monitor the operation of the isdn daemon,
+.Xr isdnd 8 ,
+which manages all ISDN related connection and disconnection of ISDN
+devices supported by the isdn4bsd package.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Switch to (curses-) fullscreen mode of operation. In this mode,
+.Nm
+behaves nearly exactly as
+.Xr isdnd 8
+in fullscreen mode. In fullscreen mode, entering the control character
+.Em Control-L
+causes the display to be refreshed and entering
+.Em Carriage-Return
+or
+.Em Enter
+will pop-up a command window. Because
+.Nm
+will not listen to messages while the command window is active,
+this command window will disappear automatically after 5 seconds without
+any command key press.
+.Pp
+While the command window is active,
+.Em Tab
+or
+.Em Space
+advances to the next menu item. To execute a command, press
+.Em Return
+or
+.Em Enter
+for the highlighted menu item, or enter the number corresponding to the
+item to be executed or enter the capitalized character in the menu item
+description.
+.It Fl d
+If debugging support is compiled into
+.Nm
+this option is used to specify the debugging level.
+.\" The debugging level is the sum of the
+.\" following values:
+.\" .Pp
+.\" .Bl -tag -width Ds -compact -offset indent
+.\" .It Ar 0x001
+.\" general debugging.
+.\" .It Ar 0x002
+.\" rates calculation.
+.\" .It Ar 0x004
+.\" timing calculations.
+.\" .It Ar 0x008
+.\" state transitions.
+.\" .It Ar 0x010
+.\" retry handling.
+.\" .It Ar 0x020
+.\" dialing.
+.\" .It Ar 0x040
+.\" process handling.
+.\" .It Ar 0x080
+.\" isdn4bsd kernel i/o calls.
+.\" .It Ar 0x100
+.\" controller and channel busy/free messages.
+.\" .It Ar 0x200
+.\" isdnmonitor.rc configuration file processing.
+.\" .El
+.\" .Pp
+.\" The value can be specified in any number base supported by the
+.\" .Xr sscanf 3
+.\" library routine.
+.Pp
+In addition, this option accepts also the character 'n' as an argument to
+disable displaying debug messages on the full-screen display.
+.Pp
+.It Fl f
+Specifying this option causes
+.Nm
+to write its normal output and - if enabled - debugging output to a file
+which name is specified as the argument.
+.It Fl l
+is used to specify a Unix local domain socket name to be used for communication
+between
+.Xr isdnd 8
+and
+.Nm .
+.It Fl h
+is used to specify a hostname or a dotted-quad IP address of a machine
+where an
+.Xr isdnd 8
+is running which should be monitored.
+.It Fl p
+This option may be used to specify a remote port number in conjunction
+with the
+.Fl h
+option.
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width Ds
+.It Ev TERM
+The terminal type when running in full-screen display mode.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh EXAMPLES
+For a first try, the following command should be used to start
+.Nm
+to monitor a locally running isdnd:
+.Bd -literal -offset indent
+isdnmonitor -h localhost
+.Ed
+.Sh DIAGNOSTICS
+Exit status is 0 on success, 1 on error.
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh BUGS
+Still one (or) more left.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Martin Husemann
+and
+.An Hellmuth Michaelis Aq hm@kts.org .
+This manual page was written by
+.An Hellmuth Michaelis .
diff --git a/usr.sbin/i4b/isdnmonitor/main.c b/usr.sbin/i4b/isdnmonitor/main.c
new file mode 100644
index 0000000..1a0a1e89
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/main.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 1998,1999 Martin Husemann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - network monitor client
+ * -----------------------------------
+ *
+ * $Id: main.c,v 1.35 2000/08/24 11:48:57 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:11 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#ifndef WIN32
+#include <unistd.h>
+#include <netdb.h>
+#endif
+#include <sys/types.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#else
+#include <stdarg.h>
+#include <windows.h>
+extern char *optarg;
+int getopt(int nargc, char * const nargv[], const char *ostr);
+#define close(f) closesocket(f)
+#define sleep(s) Sleep(s*1000)
+#define vsnprintf _vsnprintf
+#define ssize_t long
+#endif
+#ifdef ERROR
+#undef ERROR
+#endif
+
+#define MAIN
+#include "monprivate.h"
+#undef MAIN
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+#ifdef DEBUG
+#include <ctype.h>
+#endif
+
+#include "monitor.h"
+
+/*
+ * Local function prototypes
+ */
+static int connect_local(char *sockpath);
+static int connect_remote(char *host, int portno);
+static void usage();
+static void mloop();
+static void handle_input();
+static void print_menu();
+static void print_logevent(time_t tstamp, int prio, char * what, char * msg);
+static void print_charge(time_t tstamp, int controller, int channel, int units, int estimated);
+static void print_connect(time_t tstamp, int dir, int controller, int channel, char * cfgname, char * devname, char * remphone, char * locphone);
+static void print_disconnect(time_t tstamp, int controller, int channel);
+static void print_updown(time_t tstamp, int contoller, int channel, int isup);
+static void handle_event(u_int8_t *msg, int len);
+#ifdef DEBUG
+static void dump_event(u_int8_t *msg, int len, int readflag);
+#endif
+
+static ssize_t sock_read(int fd, void *buf, size_t nbytes);
+static ssize_t sock_write(int fd, void *buf, size_t nbytes);
+
+static void mprintf(char *fmt, ...);
+
+/*
+ * Global variables
+ */
+static int debug = 0;
+#define DBG_DUMPALL 0x01
+#define DBG_PSEND 0x02
+
+static int monsock = -1;
+static int state = ST_INIT;
+static int sub_state = 0;
+static int sub_state_count = 0;
+
+static int isdn_major = 0;
+static int isdn_minor = 0;
+static u_int32_t rights = 0;
+
+static char *logfilename = NULL;
+static FILE *lfp = NULL;
+
+/*---------------------------------------------------------------------------
+ * Display usage and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage()
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdnmonitor - version %02d.%02d.%d, %s %s (protocol %02d.%02d)\n", VERSION, REL, STEP, __DATE__, __TIME__, MPROT_VERSION, MPROT_REL);
+#ifdef FOREIGN
+ fprintf(stderr, " usage: isdnmonitor [-c] [-d val] [-f name] [-h host] [-p port]\n");
+#else
+ fprintf(stderr, " usage: isdnmonitor [-c] [-d val] [-f name] [-h host] [-l path] [-p port]\n");
+#endif
+ fprintf(stderr, " -c switch to curses fullscreen output\n");
+ fprintf(stderr, " -d <val> debug flags (see source ...)\n");
+ fprintf(stderr, " -dn no debug output on fullscreen display\n");
+ fprintf(stderr, " -f <name> filename to log output to\n");
+ fprintf(stderr, " -h <host> hostname/address to connect to\n");
+#ifndef FOREIGN
+ fprintf(stderr, " -l <path> pathname to local domain socket to connect to\n");
+#endif
+ fprintf(stderr, " -p <port> portnumber to use to connect to remote host\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------
+ * Parse command line, startup monitor client
+ *---------------------------------------------------------------------------*/
+int main(int argc, char **argv)
+{
+ int i;
+
+#ifdef WIN32
+ WSADATA wsCaps;
+ WSAStartup(MAKEWORD(2, 0), &wsCaps);
+#endif
+
+ portno = DEF_MONPORT;
+ devbuf[0] = '\0';
+
+#ifndef FOREIGN
+ while((i = getopt(argc, argv, "cd:f:h:p:l:")) != -1)
+#else
+ while((i = getopt(argc, argv, "cd:f:h:p:")) != -1)
+#endif
+ {
+ switch(i)
+ {
+ case 'c':
+ fullscreen = 1;
+ break;
+ case 'd':
+ if(*optarg == 'n')
+ {
+ debug_noscreen = 1;
+ }
+ else
+ {
+ if((sscanf(optarg, "%i", &debug)) != 1)
+ usage();
+ }
+ break;
+ case 'f':
+ logfilename = optarg;
+ break;
+ case 'h':
+ hostname = optarg;
+ break;
+#ifndef FOREIGN
+ case 'l':
+ sockpath = optarg;
+ break;
+#endif
+ case 'p':
+ if((sscanf(optarg, "%i", &portno)) != 1)
+ usage();
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+#ifndef FOREIGN
+ if(hostname && sockpath)
+ {
+ fprintf(stderr, "Error: can not use local socket path on remote machine\n"
+ "conflicting options -h and -l!\n");
+ return 1;
+ }
+
+ if(sockpath)
+ {
+ monsock = connect_local(sockpath);
+ }
+ else if(hostname)
+#else
+ if(hostname)
+#endif
+
+ {
+ monsock = connect_remote(hostname, portno);
+ }
+ else
+ {
+ usage();
+ }
+
+ if(monsock == -1)
+ {
+ fprintf(stderr, "Could not connect to i4b isdn daemon.\n");
+ return 1;
+ }
+
+ if(logfilename != NULL)
+ {
+ if((lfp = fopen(logfilename, "w")) == NULL)
+ {
+ fprintf(stderr, "could not open logfile [%s], %s\n", logfilename, strerror(errno));
+ exit(1);
+ }
+ }
+
+#ifndef WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ mloop();
+
+ close(monsock);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * Connect via tcp/ip.
+ * Return socket if successfull, -1 on error.
+ ---------------------------------------------------------------------------*/
+static int
+connect_remote(char *host, int portno)
+{
+ struct sockaddr_in sa;
+ struct hostent *h;
+ int remotesockfd;
+
+ h = gethostbyname(host);
+
+ if(!h)
+ {
+ fprintf(stderr, "could not resolve hostname '%s'\n", host);
+ exit(1);
+ }
+
+ remotesockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ if(remotesockfd == -1)
+ {
+ fprintf(stderr, "could not create remote monitor socket: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+
+#ifdef BSD4_4
+ sa.sin_len = sizeof(sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(portno);
+
+ memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], sizeof(sa.sin_addr.s_addr));
+
+ if(connect(remotesockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
+ {
+ fprintf(stderr, "could not connect remote monitor: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ return remotesockfd;
+}
+
+#ifndef FOREIGN
+/*---------------------------------------------------------------------------
+ * Connect local.
+ * Return socket on success, -1 on failure.
+ *---------------------------------------------------------------------------*/
+static int
+connect_local(char *sockpath)
+{
+ int s;
+ struct sockaddr_un sa;
+
+ /* check path length */
+ if(strlen(sockpath) >= sizeof(sa.sun_path))
+ {
+ fprintf(stderr, "pathname to long for local socket: %s\n",
+ sockpath);
+ exit(1);
+ }
+
+ /* create and setup socket */
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+ if(s == -1)
+ {
+ fprintf(stderr, "could not create local monitor socket:%s\n", strerror(errno));
+ exit(1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sun_len = sizeof(sa);
+ sa.sun_family = AF_LOCAL;
+ strcpy(sa.sun_path, sockpath);
+
+ if(connect(s, (struct sockaddr *)&sa, sizeof(sa)))
+ {
+ fprintf(stderr, "could not connect local monitor socket [%s]: %s\n", sockpath, strerror(errno));
+ }
+
+ return s;
+}
+#endif
+
+/*---------------------------------------------------------------------------*
+ * data from keyboard available, read and process it
+ *---------------------------------------------------------------------------*/
+#ifndef WIN32
+static void
+kbdrdhdl(void)
+{
+ int ch = getch();
+
+ switch(ch)
+ {
+ case 0x0c: /* control L */
+ wrefresh(curscr);
+ break;
+
+ case '\n':
+ case '\r':
+ do_menu();
+ break;
+ }
+}
+#endif
+
+/*---------------------------------------------------------------------------
+ * main event loop
+ *---------------------------------------------------------------------------*/
+static void
+mloop()
+{
+ for(;;)
+ {
+ fd_set rd, wr, ex;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wr);
+ FD_ZERO(&ex);
+ FD_SET(fileno(stdin), &rd);
+ FD_SET(monsock, &rd);
+
+ select(monsock+1, &rd, &wr, &ex, NULL);
+
+ if(FD_ISSET(fileno(stdin), &rd))
+ {
+#ifndef WIN32
+ if(fullscreen && curses_ready)
+ kbdrdhdl();
+ else
+#endif
+ if(!fullscreen)
+ handle_input();
+ else
+ getchar();
+ }
+
+ if(FD_ISSET(monsock, &rd))
+ {
+ u_int8_t buf[8192];
+ int bytes, ret;
+
+ /* Network transfer may deliver two or more packets concatenated.
+ * Peek at the header and read only one event at a time... */
+
+ bytes = recv(monsock, buf, I4B_MON_EVNT_HDR, MSG_PEEK);
+
+ if(bytes == 0)
+ {
+ close(monsock);
+
+#ifndef WIN32
+ if(curses_ready)
+ {
+ endwin();
+ curses_ready = 0;
+ }
+#endif
+
+ mprintf("remote isdnd has closed our connection\n");
+ exit(0);
+ }
+ else if(bytes < 0)
+ {
+ fprintf(stderr, "recv error: %s\n", strerror(errno));
+ close(monsock);
+ exit(1);
+ }
+
+ if (bytes < I4B_MON_EVNT_HDR)
+ continue; /* errh? something must be wrong... */
+
+ bytes = I4B_GET_2B(buf, I4B_MON_EVNT_LEN);
+
+ if(bytes >= sizeof(buf))
+ {
+ fprintf(stderr, "mloop: socket recv buffer overflow %d!\n", bytes);
+ break;
+ }
+
+ /* now we know the size, it fits, so lets read it! */
+
+ ret = sock_read(monsock, buf, bytes);
+
+ if(ret == 0)
+ {
+ close(monsock);
+#ifndef WIN32
+ if(curses_ready)
+ endwin();
+#endif
+ mprintf("remote isdnd has closed our connection\n");
+ exit(0);
+ }
+ else if(ret < 0)
+ {
+ mprintf("error reading from isdnd: %s", strerror(errno));
+ break;
+ }
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(buf, ret, 1);
+#endif
+ handle_event(buf, ret);
+ }
+ }
+}
+
+#ifdef DEBUG
+/*
+ * Dump a complete event packet.
+ */
+static void dump_event(u_int8_t *msg, int len, int read)
+{
+ int i;
+
+ if(read)
+ mprintf("read from socket:");
+ else
+ mprintf("write to socket:");
+
+ for(i = 0; i < len; i++)
+ {
+ if(i % 8 == 0)
+ mprintf("\n%02d: ", i);
+ mprintf("0x%02x %c ", msg[i], isprint(msg[i]) ? msg[i] : '.');
+ }
+ mprintf("\n");
+}
+#endif
+
+static void
+print_logevent(time_t tstamp, int prio, char * what, char * msg)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+ mprintf("log: %s prio %d what=%s msg=%s\n", buf, prio, what, msg);
+
+#ifndef WIN32
+ if(fullscreen)
+ {
+ if((!debug_noscreen) || (debug_noscreen && (((strcmp(what, "DBG"))) != 0)))
+ {
+/*
+ * FreeBSD-current integrated ncurses. Since then it is no longer possible
+ * to write to the last column in the logfilewindow without causing an
+ * automatic newline to occur resulting in a blank line in that window.
+ */
+#ifdef __FreeBSD__
+#include <osreldate.h>
+#endif
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 400009
+#warning "FreeBSD ncurses is buggy: write to last column = auto newline!"
+ wprintw(lower_w, "%s %s %-.*s\n", buf, what,
+ COLS-((strlen(buf))+(strlen(what))+3), msg);
+#else
+ wprintw(lower_w, "%s %s %-.*s\n", buf, what,
+ (int)(COLS-((strlen(buf))+(strlen(what))+2)), msg);
+#endif
+ wrefresh(lower_w);
+ }
+ }
+#endif
+}
+
+static void
+print_charge(time_t tstamp, int controller, int channel, int units, int estimated)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+ mprintf("%s: controller %d, channel %d, charge = %d%s\n",
+ buf, controller, channel, units, estimated ? " (estimated)" : "");
+#ifndef WIN32
+ if(fullscreen)
+ {
+ if(estimated)
+ display_ccharge(CHPOS(controller, channel), units);
+ else
+ display_charge(CHPOS(controller, channel), units);
+ }
+#endif
+}
+
+/*
+ * Print a connect event.
+ * A real monitor would allocate state info for "channel" on this
+ * event.
+ */
+static void print_connect(
+ time_t tstamp, /* server time of event */
+ int outgoing, /* 0 = incoming, 1 = outgoing */
+ int controller, /* controller number */
+ int channel, /* channel no, used to identify this connection until disconnect */
+ char * cfgname, /* name of config entry/connection */
+ char * devname, /* device used (e.g. isp0) */
+ char * remphone, /* phone no of remote side */
+ char * locphone) /* local phone no */
+{
+ char buf[256];
+
+ if(channel == 0)
+ remstate[controller].ch1state = 1;
+ else
+ remstate[controller].ch2state = 1;
+
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ if(outgoing)
+ mprintf("%s: calling out to '%s' [from msn: '%s']",
+ buf, remphone, locphone);
+ else
+ mprintf("%s: incoming call from '%s' [to msn: '%s']",
+ buf, remphone, locphone);
+ mprintf(", controller %d, channel %d, config '%s' on device '%s'\n",
+ controller, channel, cfgname, devname);
+
+#ifndef WIN32
+ if(fullscreen)
+ display_connect(CHPOS(controller, channel), outgoing, cfgname, remphone, devname);
+#endif
+}
+
+/*
+ * Print a disconnect event.
+ * A real monitor could free the "per connection" state
+ * for this channel now
+ */
+static void
+print_disconnect(time_t tstamp, int controller, int channel)
+{
+ char buf[256];
+
+ if(channel == 0)
+ remstate[controller].ch1state = 0;
+ else
+ remstate[controller].ch2state = 0;
+
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: controller %d, channel %d disconnected\n",
+ buf, controller, channel);
+
+#ifndef WIN32
+ if(fullscreen)
+ display_disconnect(CHPOS(controller, channel));
+#endif
+}
+
+/*
+ * Print an up- or down event
+ */
+static void
+print_updown(time_t tstamp, int controller, int channel, int isup)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+ mprintf("%s: channel %d is %s\n",
+ buf, channel, isup ? "up" : "down");
+}
+
+/*
+ * Print l1 / l2 status
+ */
+static void
+print_l12stat(time_t tstamp, int controller, int layer, int state)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: layer %d change on controller %d: %s\n",
+ buf, layer, controller, state ? "up" : "down");
+#ifndef WIN32
+ if(fullscreen)
+ display_l12stat(controller, layer, state);
+#endif
+}
+
+/*
+ * Print TEI
+ */
+static void
+print_tei(time_t tstamp, int controller, int tei)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: controller %d, TEI is %d\n",
+ buf, controller, tei);
+
+#ifndef WIN32
+ if(fullscreen)
+ display_tei(controller, tei);
+#endif
+}
+
+/*
+ * Print accounting information
+ */
+static void
+print_acct(time_t tstamp, int controller, int channel, int obytes, int obps,
+ int ibytes, int ibps)
+{
+ char buf[256];
+ strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
+
+ mprintf("%s: controller %d, channel %d: %d obytes, %d obps, %d ibytes, %d ibps\n",
+ buf, controller, channel, obytes, obps, ibytes, ibps);
+#ifndef WIN32
+ if(fullscreen)
+ display_acct(CHPOS(controller, channel), obytes, obps, ibytes, ibps);
+#endif
+}
+
+static void
+print_initialization(void)
+{
+#ifndef WIN32
+ if(fullscreen)
+ {
+ if(curses_ready == 0)
+ init_screen();
+ }
+ else
+#endif
+ {
+ print_menu();
+ }
+}
+
+/*
+ * Dispatch one message received from the daemon.
+ */
+static void
+handle_event(u_int8_t *msg, int len)
+{
+ u_int8_t cmd[I4B_MON_ICLIENT_SIZE];
+ int local;
+ u_int32_t net;
+ u_int32_t mask;
+ u_int32_t who;
+ static int first = 1;
+
+ switch(state)
+ {
+ case ST_INIT: /* initial data */
+
+ isdn_major = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMAJOR);
+ isdn_minor = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMINOR);
+ nctrl = I4B_GET_2B(msg, I4B_MON_IDATA_NUMCTRL);
+ nentries = I4B_GET_2B(msg, I4B_MON_IDATA_NUMENTR);
+ rights = I4B_GET_4B(msg, I4B_MON_IDATA_CLACCESS);
+
+ mprintf("remote protocol version is %02d.%02d\n", isdn_major, isdn_minor);
+
+ if(isdn_major != MPROT_VERSION || isdn_minor != MPROT_REL)
+ {
+ fprintf(stderr, "ERROR, remote protocol version mismatch:\n");
+ fprintf(stderr, "\tremote major version is %02d, local major version is %02d\n", isdn_major, MPROT_VERSION);
+ fprintf(stderr, "\tremote minor version is %02d, local minor version is %02d\n", isdn_minor, MPROT_REL);
+ exit(1);
+ }
+
+ mprintf("our rights = 0x%x\n", rights);
+
+ sub_state = 0;
+ first = 1;
+
+ if(nctrl > 0)
+ {
+ state = ST_ICTRL;
+ }
+ else if(nentries > 0)
+ {
+ state = ST_IDEV;
+ }
+ else
+ {
+ state = ST_ANYEV;
+ sleep(2);
+ print_initialization();
+ }
+
+ /* set maximum event mask */
+ I4B_PREP_CMD(cmd, I4B_MON_CCMD_SETMASK);
+ I4B_PUT_2B(cmd, I4B_MON_ICLIENT_VERMAJOR, MPROT_VERSION);
+ I4B_PUT_2B(cmd, I4B_MON_ICLIENT_VERMINOR, MPROT_REL);
+ I4B_PUT_4B(cmd, I4B_MON_ICLIENT_EVENTS, ~0U);
+
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, sizeof(cmd), 0);
+#endif
+
+ if((sock_write(monsock, cmd, sizeof(cmd))) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ break;
+
+ case ST_ICTRL: /* initial controller list */
+ if(first)
+ {
+ first = 0;
+ mprintf("%d controller(s) found:\n", nctrl);
+ }
+ mprintf("\tcontroller %d: %s\n", sub_state++, msg+I4B_MON_ICTRL_NAME);
+
+ if(sub_state >= nctrl)
+ {
+ sub_state = 0;
+ first = 1;
+ if(nentries > 0)
+ {
+ state = ST_IDEV; /* end of list reached */
+ }
+ else
+ {
+ state = ST_ANYEV;
+ sleep(2);
+ print_initialization();
+ }
+ }
+ break;
+
+ case ST_IDEV: /* initial entry devicename list */
+ if(first)
+ {
+ first = 0;
+ mprintf("%d entries found:\n", nentries);
+ }
+
+ mprintf("\tentry %d: device %s\n", sub_state++, msg+I4B_MON_IDEV_NAME);
+
+ strcat(devbuf, msg+I4B_MON_IDEV_NAME);
+ /* strcat(devbuf, " "); */
+
+ if(sub_state >= nentries)
+ {
+ first = 1;
+ state = ST_ANYEV; /* end of list reached */
+ sub_state = 0;
+ sleep(2);
+ print_initialization();
+ }
+ break;
+
+ case ST_ANYEV: /* any event */
+ switch(I4B_GET_2B(msg, I4B_MON_EVNT))
+ {
+ case I4B_MON_DRINI_CODE:
+ state = ST_RIGHT; /* list of rights entries will follow */
+ sub_state = 0;
+ sub_state_count = I4B_GET_2B(msg, I4B_MON_DRINI_COUNT);
+ mprintf("monitor rights:\n");
+ break;
+
+ case I4B_MON_DCINI_CODE:
+ state = ST_CONNS;
+ sub_state = 0;
+ sub_state_count = I4B_GET_2B(msg, I4B_MON_DCINI_COUNT);
+ mprintf("monitor connections:\n");
+ break;
+
+ case I4B_MON_LOGEVNT_CODE:
+ print_logevent(I4B_GET_4B(msg, I4B_MON_LOGEVNT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_LOGEVNT_PRIO),
+ msg+I4B_MON_LOGEVNT_WHAT,
+ msg+I4B_MON_LOGEVNT_MSG);
+ break;
+
+ case I4B_MON_CHRG_CODE:
+ print_charge(I4B_GET_4B(msg, I4B_MON_CHRG_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_CHRG_CTRL),
+ I4B_GET_4B(msg, I4B_MON_CHRG_CHANNEL),
+ I4B_GET_4B(msg, I4B_MON_CHRG_UNITS),
+ I4B_GET_4B(msg, I4B_MON_CHRG_ESTIMATED));
+ break;
+
+ case I4B_MON_CONNECT_CODE:
+ print_connect(
+ I4B_GET_4B(msg, I4B_MON_CONNECT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_CONNECT_DIR),
+ I4B_GET_4B(msg, I4B_MON_CONNECT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_CONNECT_CHANNEL),
+ msg+I4B_MON_CONNECT_CFGNAME,
+ msg+I4B_MON_CONNECT_DEVNAME,
+ msg+I4B_MON_CONNECT_REMPHONE,
+ msg+I4B_MON_CONNECT_LOCPHONE);
+ break;
+
+ case I4B_MON_DISCONNECT_CODE:
+ print_disconnect(
+ I4B_GET_4B(msg, I4B_MON_DISCONNECT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_DISCONNECT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_DISCONNECT_CHANNEL));
+ break;
+
+ case I4B_MON_UPDOWN_CODE:
+ print_updown(
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_CTRL),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_CHANNEL),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_ISUP));
+ break;
+ case I4B_MON_L12STAT_CODE:
+ print_l12stat(
+ I4B_GET_4B(msg, I4B_MON_L12STAT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_L12STAT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_L12STAT_LAYER),
+ I4B_GET_4B(msg, I4B_MON_L12STAT_STATE));
+ break;
+ case I4B_MON_TEI_CODE:
+ print_tei(
+ I4B_GET_4B(msg, I4B_MON_TEI_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_TEI_CTRL),
+ I4B_GET_4B(msg, I4B_MON_TEI_TEI));
+ break;
+ case I4B_MON_ACCT_CODE:
+ print_acct(
+ I4B_GET_4B(msg, I4B_MON_ACCT_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_ACCT_CTRL),
+ I4B_GET_4B(msg, I4B_MON_ACCT_CHAN),
+ I4B_GET_4B(msg, I4B_MON_ACCT_OBYTES),
+ I4B_GET_4B(msg, I4B_MON_ACCT_OBPS),
+ I4B_GET_4B(msg, I4B_MON_ACCT_IBYTES),
+ I4B_GET_4B(msg, I4B_MON_ACCT_IBPS));
+ break;
+ default:
+ mprintf("unknown event code: %d\n", I4B_GET_2B(msg, I4B_MON_EVNT));
+ }
+ break;
+
+ case ST_RIGHT: /* one record in a list of monitor rights */
+ rights = I4B_GET_4B(msg, I4B_MON_DR_RIGHTS);
+ net = I4B_GET_4B(msg, I4B_MON_DR_NET);
+ mask = I4B_GET_4B(msg, I4B_MON_DR_MASK);
+ local = I4B_GET_1B(msg, I4B_MON_DR_LOCAL);
+
+ if(local)
+ {
+ mprintf("\tlocal: rights = %x\n", rights);
+ }
+ else
+ {
+ mprintf("\tfrom: %d.%d.%d.%d, mask %d.%d.%d.%d, rights = %x\n",
+ (net >> 24) & 0x00ff, (net >> 16) & 0x00ff, (net >> 8) & 0x00ff, net & 0x00ff,
+ (mask >> 24) & 0x00ff, (mask >> 16) & 0x00ff, (mask >> 8) & 0x00ff, mask & 0x00ff,
+ rights);
+ }
+
+ sub_state++;
+
+ if(sub_state >= sub_state_count)
+ {
+ state = ST_ANYEV;
+ print_initialization();
+ }
+ break;
+
+ case ST_CONNS:
+ who = I4B_GET_4B(msg, I4B_MON_DC_WHO);
+ rights = I4B_GET_4B(msg, I4B_MON_DC_RIGHTS);
+
+ mprintf("\tfrom: %d.%d.%d.%d, rights = %x\n",
+ (who >> 24) & 0x00ff, (who >> 16) & 0x00ff, (who >> 8) & 0x00ff, who & 0x00ff,
+ rights);
+
+ sub_state++;
+
+ if(sub_state >= sub_state_count)
+ {
+ state = ST_ANYEV;
+ print_initialization();
+ }
+ break;
+
+ default:
+ mprintf("unknown event from remote: local state = %d, evnt = %x, len = %d\n",
+ state, I4B_GET_2B(msg, I4B_MON_EVNT), len);
+ }
+}
+
+/*
+ * Process input from user
+ */
+static void
+handle_input()
+{
+ char buf[1024];
+ int channel, controller;
+
+ fgets(buf, sizeof(buf), stdin);
+
+ switch(atoi(buf))
+ {
+ case 1:
+ {
+ u_int8_t cmd[I4B_MON_DUMPRIGHTS_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_DUMPRIGHTS_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_DUMPRIGHTS_SIZE, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_DUMPRIGHTS_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 2:
+ {
+ u_int8_t cmd[I4B_MON_DUMPMCONS_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_DUMPMCONS_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_DUMPMCONS_CODE, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_DUMPMCONS_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 3:
+ {
+ u_int8_t cmd[I4B_MON_CFGREREAD_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_CFGREREAD_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_CFGREREAD_CODE, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_CFGREREAD_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 4:
+ {
+ u_int8_t cmd[I4B_MON_HANGUP_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_HANGUP_CODE);
+
+ printf("Which controller you wish to hangup? ");
+ fgets(buf, sizeof(buf), stdin);
+ controller = atoi(buf);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CTRL, controller);
+
+ printf("Which channel do you wish to hangup? ");
+ fgets(buf, sizeof(buf), stdin);
+ channel = atoi(buf);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CHANNEL, channel);
+
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_HANGUP_CHANNEL, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_HANGUP_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+ break;
+
+ case 9:
+ close(monsock);
+ exit(0);
+ break;
+
+ default:
+ print_menu();
+ break;
+ }
+}
+
+void
+reread(void)
+{
+ u_int8_t cmd[I4B_MON_CFGREREAD_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_CFGREREAD_CODE);
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_CFGREREAD_CODE, 0);
+#endif
+ if((sock_write(monsock, cmd, I4B_MON_CFGREREAD_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
+void
+hangup(int ctrl, int chan)
+{
+ u_int8_t cmd[I4B_MON_HANGUP_SIZE];
+
+ I4B_PREP_CMD(cmd, I4B_MON_HANGUP_CODE);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CTRL, ctrl);
+ I4B_PUT_4B(cmd, I4B_MON_HANGUP_CHANNEL, chan);
+
+#ifdef DEBUG
+ if(debug & DBG_DUMPALL)
+ dump_event(cmd, I4B_MON_HANGUP_CHANNEL, 0);
+#endif
+
+ if((sock_write(monsock, cmd, I4B_MON_HANGUP_SIZE)) == -1)
+ {
+ fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
+/*
+ * Display menu
+ */
+static void
+print_menu()
+{
+ if(!fullscreen)
+ {
+ printf("Menu: <1> display rights, <2> display monitor connections,\n");
+ printf(" <3> reread config file, <4> hangup \n");
+ printf(" <9> quit isdnmonitor\n");
+ fflush(stdout);
+ }
+}
+
+static ssize_t
+sock_read(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nread;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nread = read(fd, ptr, nleft)) < 0)
+ {
+ if(errno == EINTR)
+ {
+ nread = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+ else if(nread == 0)
+ {
+ break; /* EOF */
+ }
+
+ nleft -= nread;
+ ptr += nread;
+ }
+ return(nbytes - nleft);
+}
+
+static ssize_t
+sock_write(int fd, void *buf, size_t nbytes)
+{
+ size_t nleft;
+ ssize_t nwritten;
+ unsigned char *ptr;
+
+ ptr = buf;
+ nleft = nbytes;
+
+ while(nleft > 0)
+ {
+ if((nwritten = write(fd, ptr, nleft)) <= 0)
+ {
+ if(errno == EINTR)
+ {
+ nwritten = 0;
+ }
+ else
+ {
+ return(-1);
+ }
+ }
+
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+ return(nbytes);
+}
+
+static void
+mprintf(char *fmt, ...)
+{
+#define PRBUFLEN 1024
+ char buffer[PRBUFLEN];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, PRBUFLEN-1, fmt, ap);
+ va_end(ap);
+
+ if(!fullscreen || (fullscreen && (!curses_ready)))
+ printf("%s", buffer);
+
+ if(logfilename != NULL)
+ {
+ fprintf(lfp, "%s", buffer);
+ fflush(lfp);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnmonitor/monitor.h b/usr.sbin/i4b/isdnmonitor/monitor.h
new file mode 100644
index 0000000..4862c48
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/monitor.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 1998,1999 Martin Husemann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b daemon - network monitor protocol definition
+ * ------------------------------------------------
+ *
+ * $Id: monitor.h,v 1.16 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:18 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _MONITOR_H_
+#define _MONITOR_H_
+
+#define DEF_MONPORT 451 /* default monitor TCP port */
+
+#ifdef __hpux
+#define u_int8_t ubit8
+#define u_int32_t ubit32
+#endif
+#ifdef WIN32
+#define u_int8_t BYTE
+#define u_int32_t DWORD
+#endif
+
+/*
+ * The monitor client connects to the isdnd daemon process via a tcp/ip
+ * connection from a remote machine or via a local (unix domain) socket.
+ * The daemon accepts multiple connections and verifies access rights.
+ * On connection establishment the daemon sends initial data telling
+ * the client the current configuration: number and type of available
+ * controllers, current connections, channel and interface states
+ * and the clients access privileges. The client sends an event mask
+ * telling the daemon which events it is interested in. If the client
+ * has appropriate rights he may send commands to the daemon.
+ *
+ * All multi-byte values are in network byte order!
+ */
+
+/* All data packets transfered are declared as arrays of u_int8_t */
+
+/* max stringlength used in this protocol */
+#define I4B_MAX_MON_STRING 256
+
+/* max command size from client to server */
+#define I4B_MAX_MON_CLIENT_CMD 16
+
+/* Version of the monitor protocol described here */
+#define MPROT_VERSION 0 /* major version no */
+#define MPROT_REL 5 /* release no */
+
+/*
+ * Client access rights
+ */
+#define I4B_CA_COMMAND_FULL 1 /* may send any command */
+#define I4B_CA_COMMAND_RESTRICTED 2 /* may send 'harmless' commands */
+#define I4B_CA_EVNT_CHANSTATE 16 /* may watch b-channel states */
+#define I4B_CA_EVNT_CALLIN 32 /* may watch incoming calls */
+#define I4B_CA_EVNT_CALLOUT 64 /* may watch outgoing calls */
+#define I4B_CA_EVNT_I4B 128 /* may watch isdnd actions */
+
+/*
+ * General layout of a command packet. All commands have this common
+ * prefix. It is prepared by the macro I4B_PREP_CMD (s.b.)
+ */
+#define I4B_MON_CMD 0 /* 2 byte: command code */
+#define I4B_MON_CMD_LEN 2 /* 2 byte: packet length */
+#define I4B_MON_CMD_HDR 4 /* size of header */
+
+/*
+ * Currently events look the same as commands. We do not make
+ * any guarantee this will remain the same, so a different set
+ * of macros is used when describing events. Events are prepared
+ * by I4B_PREP_EVNT (s.b.)
+ */
+#define I4B_MON_EVNT 0 /* 2 byte: event code */
+#define I4B_MON_EVNT_LEN 2 /* 2 byte: packet length */
+#define I4B_MON_EVNT_HDR 4 /* size of header */
+
+/* Initial data send by daemon after connection is established */
+#define I4B_MON_IDATA_SIZE I4B_MON_EVNT_HDR+12
+#define I4B_MON_IDATA_CODE 0 /* event code */
+#define I4B_MON_IDATA_VERSMAJOR I4B_MON_EVNT_HDR+0 /* 2 byte: isdnd major version */
+#define I4B_MON_IDATA_VERSMINOR I4B_MON_EVNT_HDR+2 /* 2 byte: isdnd minor version */
+#define I4B_MON_IDATA_NUMCTRL I4B_MON_EVNT_HDR+4 /* 2 byte: number of controllers */
+#define I4B_MON_IDATA_NUMENTR I4B_MON_EVNT_HDR+6 /* 2 byte: number of controllers */
+#define I4B_MON_IDATA_CLACCESS I4B_MON_EVNT_HDR+8 /* 4 byte: client rights */
+
+/* followed by this for every controller */
+#define I4B_MON_ICTRL_SIZE I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+8
+#define I4B_MON_ICTRL_CODE 1 /* event code */
+#define I4B_MON_ICTRL_NAME I4B_MON_EVNT_HDR+0 /* string: name of controller */
+#define I4B_MON_ICTRL_BUSID I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+0 /* 2 byte: isdn bus id (reservered) */
+#define I4B_MON_ICTRL_FLAGS I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+2 /* 4 byte: controller flags (not yet defined) */
+#define I4B_MON_ICTRL_NCHAN I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+6 /* 2 byte: number of b channels on this controller */
+
+/* followed by this for every entry */
+#define I4B_MON_IDEV_SIZE I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+2
+#define I4B_MON_IDEV_CODE 2 /* event code */
+#define I4B_MON_IDEV_NAME I4B_MON_EVNT_HDR+0 /* string: name of device */
+#define I4B_MON_IDEV_STATE I4B_MON_EVNT_HDR+I4B_MAX_MON_STRING+0 /* 2 byte: state of device */
+
+/*
+ * The client sets it's protocol version and event mask (usually once after
+ * connection establishement)
+ */
+#define I4B_MON_CCMD_SETMASK 0x7e /* command code */
+#define I4B_MON_ICLIENT_SIZE I4B_MON_CMD_HDR+8
+#define I4B_MON_ICLIENT_VERMAJOR I4B_MON_CMD_HDR+0 /* 2 byte: protocol major version (always 0 for now) */
+#define I4B_MON_ICLIENT_VERMINOR I4B_MON_CMD_HDR+2 /* 2 byte: protocol minor version (always 0 for now) */
+#define I4B_MON_ICLIENT_EVENTS I4B_MON_CMD_HDR+4 /* 4 byte: client event mask */
+
+/* The client requests a list of monitor rights */
+#define I4B_MON_DUMPRIGHTS_CODE 1
+#define I4B_MON_DUMPRIGHTS_SIZE I4B_MON_CMD_HDR /* no parameters */
+
+/*
+ * In response to an I4B_MON_DUMPRIGHTS_CODE command, the daemon sends
+ * this event:
+ */
+#define I4B_MON_DRINI_CODE 2 /* event code */
+#define I4B_MON_DRINI_SIZE I4B_MON_EVNT_HDR+2 /* size of packet */
+#define I4B_MON_DRINI_COUNT I4B_MON_EVNT_HDR+0 /* 2 byte: number of records */
+
+/* followed by this for each record anounced above */
+#define I4B_MON_DR_CODE 3
+#define I4B_MON_DR_SIZE I4B_MON_EVNT_HDR+13
+#define I4B_MON_DR_RIGHTS I4B_MON_EVNT_HDR+0 /* 4 byte: rights mask */
+#define I4B_MON_DR_NET I4B_MON_EVNT_HDR+4 /* 4 byte: network address */
+#define I4B_MON_DR_MASK I4B_MON_EVNT_HDR+8 /* 4 byte: network mask */
+#define I4B_MON_DR_LOCAL I4B_MON_EVNT_HDR+12 /* 1 byte: non-zero if local socket */
+
+/* The client requests a list of monitor connections */
+#define I4B_MON_DUMPMCONS_CODE 2
+#define I4B_MON_DUMPMCONS_SIZE I4B_MON_CMD_HDR /* no parameters */
+
+/*
+ * In response to an I4B_MON_DUMPMCONS_CODE command, the daemon sends
+ * this event:
+ */
+#define I4B_MON_DCINI_CODE 4 /* event code */
+#define I4B_MON_DCINI_SIZE I4B_MON_EVNT_HDR+2 /* size of packet */
+#define I4B_MON_DCINI_COUNT I4B_MON_EVNT_HDR+0 /* 2 byte: number of records */
+
+/* followed by this for each record anounced above */
+#define I4B_MON_DC_CODE 5
+#define I4B_MON_DC_SIZE I4B_MON_EVNT_HDR+8
+#define I4B_MON_DC_RIGHTS I4B_MON_EVNT_HDR+0 /* 4 byte: rights mask */
+#define I4B_MON_DC_WHO I4B_MON_EVNT_HDR+4 /* 4 byte: network address */
+
+/* The client requests a config file rescan */
+#define I4B_MON_CFGREREAD_CODE 3
+#define I4B_MON_CFGREREAD_SIZE I4B_MON_CMD_HDR /* no parameters */
+
+/* The client requests to hangup a connection */
+#define I4B_MON_HANGUP_CODE 4
+#define I4B_MON_HANGUP_SIZE I4B_MON_CMD_HDR+8
+#define I4B_MON_HANGUP_CTRL I4B_MON_CMD_HDR+0 /* controller */
+#define I4B_MON_HANGUP_CHANNEL I4B_MON_CMD_HDR+4 /* channel */
+
+/* The daemon sends a logfile event */
+#define I4B_MON_LOGEVNT_CODE 6
+#define I4B_MON_LOGEVNT_SIZE I4B_MON_EVNT_HDR+8+2*I4B_MAX_MON_STRING
+#define I4B_MON_LOGEVNT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: timestamp */
+#define I4B_MON_LOGEVNT_PRIO I4B_MON_EVNT_HDR+4 /* 4 byte: syslog priority */
+#define I4B_MON_LOGEVNT_WHAT I4B_MON_EVNT_HDR+8 /* followed by 2 strings: 'what' and 'message' */
+#define I4B_MON_LOGEVNT_MSG I4B_MON_EVNT_HDR+8+I4B_MAX_MON_STRING
+
+/* The daemon sends a charge event */
+#define I4B_MON_CHRG_CODE 7
+#define I4B_MON_CHRG_SIZE I4B_MON_EVNT_HDR+20
+#define I4B_MON_CHRG_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: timestamp */
+#define I4B_MON_CHRG_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: channel charged */
+#define I4B_MON_CHRG_CHANNEL I4B_MON_EVNT_HDR+8 /* 4 byte: channel charged */
+#define I4B_MON_CHRG_UNITS I4B_MON_EVNT_HDR+12 /* 4 byte: new charge value */
+#define I4B_MON_CHRG_ESTIMATED I4B_MON_EVNT_HDR+16 /* 4 byte: 0 = charge by network, 1 = calculated estimate */
+
+/* The daemon sends a connect event */
+#define I4B_MON_CONNECT_CODE 8
+#define I4B_MON_CONNECT_SIZE I4B_MON_EVNT_HDR+16+4*I4B_MAX_MON_STRING
+#define I4B_MON_CONNECT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_CONNECT_DIR I4B_MON_EVNT_HDR+4 /* 4 byte: direction (0 = incoming, 1 = outgoing) */
+#define I4B_MON_CONNECT_CTRL I4B_MON_EVNT_HDR+8 /* 4 byte: channel connected */
+#define I4B_MON_CONNECT_CHANNEL I4B_MON_EVNT_HDR+12 /* 4 byte: channel connected */
+#define I4B_MON_CONNECT_CFGNAME I4B_MON_EVNT_HDR+16 /* name of config entry */
+#define I4B_MON_CONNECT_DEVNAME I4B_MON_EVNT_HDR+16+I4B_MAX_MON_STRING /* name of device used for connection */
+#define I4B_MON_CONNECT_REMPHONE I4B_MON_EVNT_HDR+16+2*I4B_MAX_MON_STRING /* remote phone no. */
+#define I4B_MON_CONNECT_LOCPHONE I4B_MON_EVNT_HDR+16+3*I4B_MAX_MON_STRING /* local phone no. */
+
+/* The daemon sends a disconnect event */
+#define I4B_MON_DISCONNECT_CODE 9
+#define I4B_MON_DISCONNECT_SIZE I4B_MON_EVNT_HDR+12
+#define I4B_MON_DISCONNECT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_DISCONNECT_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: channel disconnected */
+#define I4B_MON_DISCONNECT_CHANNEL I4B_MON_EVNT_HDR+8 /* 4 byte: channel disconnected */
+
+/* The daemon sends an up/down event */
+#define I4B_MON_UPDOWN_CODE 10
+#define I4B_MON_UPDOWN_SIZE I4B_MON_EVNT_HDR+16
+#define I4B_MON_UPDOWN_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_UPDOWN_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: channel disconnected */
+#define I4B_MON_UPDOWN_CHANNEL I4B_MON_EVNT_HDR+8 /* 4 byte: channel disconnected */
+#define I4B_MON_UPDOWN_ISUP I4B_MON_EVNT_HDR+12 /* 4 byte: interface is up */
+
+/* The daemon sends a L1/L2 status change event */
+#define I4B_MON_L12STAT_CODE 11
+#define I4B_MON_L12STAT_SIZE I4B_MON_EVNT_HDR+16
+#define I4B_MON_L12STAT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_L12STAT_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: controller */
+#define I4B_MON_L12STAT_LAYER I4B_MON_EVNT_HDR+8 /* 4 byte: layer */
+#define I4B_MON_L12STAT_STATE I4B_MON_EVNT_HDR+12 /* 4 byte: state */
+
+/* The daemon sends a TEI change event */
+#define I4B_MON_TEI_CODE 12
+#define I4B_MON_TEI_SIZE I4B_MON_EVNT_HDR+12
+#define I4B_MON_TEI_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_TEI_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: controller */
+#define I4B_MON_TEI_TEI I4B_MON_EVNT_HDR+8 /* 4 byte: tei */
+
+/* The daemon sends an accounting message event */
+#define I4B_MON_ACCT_CODE 13
+#define I4B_MON_ACCT_SIZE I4B_MON_EVNT_HDR+28
+#define I4B_MON_ACCT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_ACCT_CTRL I4B_MON_EVNT_HDR+4 /* 4 byte: controller */
+#define I4B_MON_ACCT_CHAN I4B_MON_EVNT_HDR+8 /* 4 byte: channel */
+#define I4B_MON_ACCT_OBYTES I4B_MON_EVNT_HDR+12 /* 4 byte: outbytes */
+#define I4B_MON_ACCT_OBPS I4B_MON_EVNT_HDR+16 /* 4 byte: outbps */
+#define I4B_MON_ACCT_IBYTES I4B_MON_EVNT_HDR+20 /* 4 byte: inbytes */
+#define I4B_MON_ACCT_IBPS I4B_MON_EVNT_HDR+24 /* 4 byte: inbps */
+
+/* macros for setup/decoding of protocol packets */
+
+/* clear a record */
+#define I4B_CLEAR(r) memset(&(r), 0, sizeof(r));
+
+/* prepare a record as event or command */
+#define I4B_PREP_EVNT(r, e) { \
+ I4B_CLEAR(r); \
+ I4B_PUT_2B(r, I4B_MON_EVNT, e); \
+ I4B_PUT_2B(r, I4B_MON_EVNT_LEN, sizeof(r)); \
+}
+#define I4B_PREP_CMD(r, c) { \
+ I4B_CLEAR(r); \
+ I4B_PUT_2B(r, I4B_MON_CMD, c); \
+ I4B_PUT_2B(r, I4B_MON_CMD_LEN, sizeof(r)); \
+}
+
+/* put 1, 2 or 4 bytes in network byte order into a record at offset off */
+#define I4B_PUT_1B(r, off, val) { ((u_int8_t*)(r))[off] = (val) & 0x00ff; }
+#define I4B_PUT_2B(r, off, val) { I4B_PUT_1B(r, off, val >> 8); I4B_PUT_1B(r, off+1, val); }
+#define I4B_PUT_4B(r, off, val) { I4B_PUT_1B(r, off, val >> 24); I4B_PUT_1B(r, off+1, val >> 16); I4B_PUT_1B(r, off+2, val >> 8); I4B_PUT_1B(r, off+3, val); }
+
+/* get 1, 2 or 4 bytes in network byte order from a record at offset off */
+#define I4B_GET_1B(r, off) (((u_int8_t*)(r))[off])
+#define I4B_GET_2B(r, off) ((((u_int8_t*)(r))[off]) << 8) | (((u_int8_t*)(r))[off+1])
+#define I4B_GET_4B(r, off) ((((u_int8_t*)(r))[off]) << 24) | ((((u_int8_t*)(r))[off+1]) << 16) | ((((u_int8_t*)(r))[off+2]) << 8) | (((u_int8_t*)(r))[off+3])
+
+/*
+ * put a string into record r at offset off, make sure it's not to long
+ * and proper terminate it
+ */
+#define I4B_PUT_STR(r, off, str) { \
+ strncpy((r)+(off), (str), I4B_MAX_MON_STRING); \
+ (r)[(off)+I4B_MAX_MON_STRING-1] = (u_int8_t)0; }
+
+#endif /* _MONITOR_H_ */
+
diff --git a/usr.sbin/i4b/isdnmonitor/monprivate.h b/usr.sbin/i4b/isdnmonitor/monprivate.h
new file mode 100644
index 0000000..ec6ec4d
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/monprivate.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * i4b remote monitor - private header
+ * -----------------------------------
+ *
+ * $Id: monprivate.h,v 1.10 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:25 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#include <syslog.h>
+#include <regex.h>
+#include <curses.h>
+#include <fcntl.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#endif
+
+/*---------------------------------------------------------------------------*
+ * definitions in i4b_ioctl.h, do something for other systems
+ *---------------------------------------------------------------------------*/
+#if defined (__FreeBSD__) || defined(__NetBSD__) || \
+ defined (__OpenBSD__) || defined(__bsdi__)
+
+#include <machine/i4b_ioctl.h>
+
+#else
+
+#define FOREIGN 1 /* we are running on a "foreign" OS */
+
+#define I4B_TIME_FORMAT "%d.%m.%Y %H:%M:%S"
+#define VERSION 0
+#define REL 0
+#define STEP 0
+
+#endif
+
+/*---------------------------------------------------------------------------*
+ * some general definitions
+ *---------------------------------------------------------------------------*/
+#define GOOD 0 /* general "good" or "ok" return*/
+#define ERROR (-1) /* general error return */
+#define WARNING (-2) /* warning return */
+#define INVALID (-1) /* an invalid integer */
+
+/*---------------------------------------------------------------------------*
+ * state definitions
+ *---------------------------------------------------------------------------*/
+#define ST_INIT 0 /* initial data */
+#define ST_ICTRL 1 /* initial controller list */
+#define ST_IDEV 2 /* initial entry devicename list */
+#define ST_ANYEV 3 /* any event */
+#define ST_RIGHT 4 /* one record in a list of monitor rights */
+#define ST_CONNS 5 /* monitor connections */
+
+/*---------------------------------------------------------------------------*
+ * curses fullscreen display definitions
+ *---------------------------------------------------------------------------*/
+/* window dimensions */
+#define UPPER_B 2 /* upper window start */
+
+/* horizontal positions for upper window */
+#define H_CNTL 0 /* controller */
+#define H_TEI 2 /* TEI */
+#define H_CHAN (H_TEI+4) /* channel */
+#define H_TELN (H_CHAN+2) /* telephone number */
+#define H_IFN (H_TELN+23) /* interfacename */
+#define H_IO (H_IFN+7) /* incoming or outgoing */
+#define H_OUT (H_IO+4) /* # of bytes out */
+#define H_OUTBPS (H_OUT+11) /* bytes per second out */
+#define H_IN (H_OUTBPS+5) /* # of bytes in */
+#define H_INBPS (H_IN+11) /* bytes per second in */
+#define H_UNITS (H_INBPS+6) /* # of charging units */
+
+/* fullscreen mode menu window */
+#define WMENU_LEN 35 /* width of menu window */
+#define WMENU_TITLE "Command" /* title string */
+#define WMENU_POSLN 10 /* menu position, line */
+#define WMENU_POSCO 5 /* menu position, col */
+#define WMITEMS 4 /* no of menu items */
+#define WMENU_HGT (WMITEMS + 4) /* menu window height */
+
+#define WREFRESH 0
+#define WHANGUP 1
+#define WREREAD 2
+#define WQUIT 3
+
+#define WMTIMEOUT 5 /* timeout in seconds */
+
+/*---------------------------------------------------------------------------*
+ * misc
+ *---------------------------------------------------------------------------*/
+#define CHPOS(uctlr, uchan) (((uctlr)*2) + (uchan))
+
+/*---------------------------------------------------------------------------*
+ * remote state
+ *---------------------------------------------------------------------------*/
+
+#define MAX_CTRL 4
+
+typedef struct remstate {
+ int ch1state;
+ int ch2state;
+} remstate_t;
+
+/*---------------------------------------------------------------------------*
+ * global variables
+ *---------------------------------------------------------------------------*/
+#ifdef MAIN
+
+remstate_t remstate[MAX_CTRL];
+
+int nctrl = 0; /* # of controllers available */
+int curses_ready = 0; /* curses initialized */
+int do_bell = 0;
+int nentries = 0;
+int fullscreen = 0;
+int debug_noscreen = 0;
+
+#ifndef WIN32
+WINDOW *upper_w; /* curses upper window pointer */
+WINDOW *mid_w; /* curses mid window pointer */
+WINDOW *lower_w; /* curses lower window pointer */
+#endif
+
+char devbuf[256];
+
+char *sockpath = NULL;
+char *hostname = NULL;
+int portno;
+
+#else /* !MAIN */
+
+remstate_t remstate[MAX_CTRL];
+
+int nctrl;
+int curses_ready;
+int do_bell;
+int nentries;
+int fullscreen;
+int debug_noscreen;
+
+WINDOW *upper_w;
+WINDOW *mid_w;
+WINDOW *lower_w;
+
+char devbuf[256];
+
+char *sockpath;
+char *hostname;
+int portno;
+
+#endif
+
+extern void do_exit ( int exitval );
+extern void do_menu ( void );
+extern void init_screen ( void );
+extern void display_charge ( int pos, int charge );
+extern void display_ccharge ( int pos, int units );
+extern void display_connect(int pos, int dir, char *name, char *remtel, char *dev);
+extern void display_acct ( int pos, int obyte, int obps, int ibyte, int ibps );
+extern void display_disconnect ( int pos );
+extern void display_updown ( int pos, int updown, char *device );
+extern void display_l12stat ( int controller, int layer, int state );
+extern void display_tei ( int controller, int tei );
+
+extern void reread(void);
+extern void hangup(int ctrl, int chan);
+
+
diff --git a/usr.sbin/i4b/isdnphone/Makefile b/usr.sbin/i4b/isdnphone/Makefile
new file mode 100644
index 0000000..3527e8c
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= isdnphone
+MAN= isdnphone.8
+SRCS= main.c display.c audio.c isdn.c
+
+DPADD= ${LIBCURSES}
+LDADD= -lcurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdnphone/audio.c b/usr.sbin/i4b/isdnphone/audio.c
new file mode 100644
index 0000000..2dc88d6
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/audio.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - audio operations
+ * ============================
+ *
+ * $Id: audio.c,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:39 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+int
+init_audio(char *audiodevice)
+{
+ int ret;
+ int fd;
+ u_long fmt = 0;
+
+ snd_chan_param pa;
+ struct snd_size sz;
+ snd_capabilities soundcaps;
+
+ if((fd = open(audiodevice, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "unable to open %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ ret = ioctl(fd, AIOGCAP, &soundcaps);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "ERROR: ioctl AIOGCAP %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ fmt = soundcaps.formats;
+
+ if((fmt & AFMT_FULLDUPLEX) && (!(fmt & AFMT_WEIRD)))
+ {
+#ifdef NOTDEF
+ if(fmt & AFMT_A_LAW)
+ {
+ play_fmt = rec_fmt = AFMT_A_LAW;
+ }
+ else
+#endif
+ if(fmt & AFMT_MU_LAW)
+ {
+ play_fmt = rec_fmt = AFMT_MU_LAW;
+ }
+ else
+ {
+ printf("sorry, A-law or u-law not supported!\n");
+ close(fd);
+ return(-1);
+ }
+ }
+ else
+ {
+ printf("no full-duplex available!\n");
+ close (fd);
+ return(-1);
+ }
+
+ pa.play_format = play_fmt;
+ pa.rec_format = rec_fmt;
+ pa.play_rate = pa.rec_rate = AUDIORATE;
+
+ ret = ioctl(fd, AIOSFMT, &pa);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "ERROR: ioctl AIOSFMT %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ sz.play_size = BCH_MAX_DATALEN;
+ sz.rec_size = BCH_MAX_DATALEN;
+
+ ret = ioctl(fd, AIOSSIZE, &sz);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "ERROR: ioctl AIOSSIZE %s: %s\n", audiodevice, strerror(errno));
+ return(-1);
+ }
+
+ return(fd);
+}
+
+/*---------------------------------------------------------------------------*
+ * audio device has speech data from microphone
+ *---------------------------------------------------------------------------*/
+void
+audio_hdlr(void)
+{
+ unsigned char buffer[BCH_MAX_DATALEN];
+ int ret;
+
+ ret = read(audiofd, buffer, BCH_MAX_DATALEN);
+
+ if(ret < 0)
+ {
+ fatal("read audio failed: %s", strerror(errno));
+ }
+
+ debug("audio_hdlr: read %d bytes\n", ret);
+
+ if(ret > 0)
+ {
+ telwrite(ret, buffer);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * write audio data to loudspeaker
+ *---------------------------------------------------------------------------*/
+void
+audiowrite(int len, unsigned char *buf)
+{
+ if((write(audiofd, buf, len)) < 0)
+ {
+ fatal("write audio failed: %s", strerror(errno));
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/defs.h b/usr.sbin/i4b/isdnphone/defs.h
new file mode 100644
index 0000000..18da037
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/defs.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - header file
+ * =======================
+ *
+ * $Id: defs.h,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:46 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include <ncurses.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_tel_ioctl.h>
+
+/* device file prefixes */
+
+#define I4BTELDEVICE "/dev/i4btel"
+#define I4BTELDDEVICE "/dev/i4bteld"
+#define AUDIODEVICE "/dev/audio"
+
+#define GOOD 0
+#define ERROR (-1)
+#define WARNING (-2)
+
+/* main window dimensions */
+
+#define MW_ROW 5
+#define MW_COL 8
+
+#define MW_WIDTH 60
+#define MW_HEIGHT 8
+
+#define DB_ROW 15
+#define DB_COL 1
+#define DB_WID 79
+#define DB_HGT 9
+
+#define MW_STATEY 2
+#define MW_STATEX 1
+#define MW_STX 10
+
+#define MW_NUMY 4
+#define MW_NUMX 1
+#define MW_NUX 10
+
+#define MW_MSGY 6
+#define MW_MSGX 1
+#define MW_MSX 10
+
+/* fullscreen mode menu window */
+
+#define WMITEMS 4 /* no of items */
+#define WMENU_LEN 18 /* window width */
+#define WMENU_HGT (WMITEMS+4) /* window height */
+#define WMENU_TITLE "Command"
+#define WMENU_POSLN 8 /* window position: lines */
+#define WMENU_POSCO 20 /* window position: columns */
+
+#define CR 0x0d
+#define LF 0x0a
+#define TAB 0x09
+#define CNTRL_D 0x04
+#define CNTRL_L 0x0c
+
+#define ST_IDLE 0
+#define ST_DIALING 1
+#define ST_ACTIVE 2
+#define ST_MAX 2
+
+#define AUDIORATE 8000
+
+#ifdef MAIN
+
+WINDOW *main_w; /* curses main window pointer */
+WINDOW *dbg_w;
+
+int curses_ready = 0; /* flag, curses display is initialized */
+int state = ST_IDLE;
+
+char *states[] = {
+ "IDLE",
+ "DIALING",
+ "ACTIVE"
+};
+
+int dialerfd = -1;
+int audiofd = -1;
+int telfd = -1;
+int curx;
+char numberbuffer[TELNO_MAX];
+
+int play_fmt = AFMT_MU_LAW;
+int rec_fmt = AFMT_MU_LAW;
+
+int opt_unit = 0;
+int opt_d = 0;
+#else
+
+extern WINDOW *main_w;
+extern WINDOW *dbg_w;
+
+extern int curses_ready;
+extern int state;
+
+extern char *states[];
+
+extern int dialerfd;
+extern int audiofd;
+extern int telfd;
+extern int curx;
+extern char numberbuffer[];
+
+extern int play_fmt;
+extern int rec_fmt;
+
+int opt_unit;
+int opt_d;
+
+#endif
+
+extern void audio_hdlr ( void );
+extern void tel_hdlr ( void );
+extern void init_mainw ( void );
+extern int init_audio ( char * );
+extern void do_menu ( void );
+extern int main ( int argc, char **argv );
+extern void do_quit ( int exitval );
+extern void fatal ( char *fmt, ... );
+extern void message ( char *fmt, ... );
+extern void do_dial ( char *number );
+extern void do_hangup ( void );
+
+extern void audiowrite ( int, unsigned char * );
+extern void telwrite ( int, unsigned char * );
+
+extern void newstate ( int newstate );
+
+int init_dial(char *device);
+void dial_hdlr(void);
+int init_tel(char *device);
+
+extern void debug ( char *fmt, ... );
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/display.c b/usr.sbin/i4b/isdnphone/display.c
new file mode 100644
index 0000000..1dff9a1
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/display.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - some display operations
+ * ===================================
+ *
+ * $Id: display.c,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:52:55 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_mainw(void)
+{
+ char buffer[512];
+
+ initscr(); /* curses init */
+
+ if((COLS < 80) || (LINES < 24))
+ fatal(0, "ERROR, minimal screensize must be 80x24, is %dx%d, terminating!", COLS, LINES);
+
+
+ if((main_w = newwin(MW_HEIGHT, MW_WIDTH, MW_ROW, MW_COL)) == NULL)
+ fatal("ERROR, curses init main window, terminating!");
+
+ if(opt_d)
+ {
+ if((dbg_w = newwin(DB_HGT, DB_WID, DB_ROW, DB_COL)) == NULL)
+ fatal("ERROR, curses init debug window, terminating!");
+ scrollok(dbg_w, TRUE);
+ }
+
+ raw(); /* raw input */
+ noecho(); /* do not echo input */
+ keypad(stdscr, TRUE); /* use special keys */
+ keypad(main_w, TRUE); /* use special keys */
+
+ box(main_w, 0, 0);
+
+ sprintf(buffer, "isdnphone %d.%d ", VERSION, REL);
+
+ wstandout(main_w);
+ mvwaddstr(main_w, 0, (MW_WIDTH / 2) - (strlen(buffer) / 2), buffer);
+ wstandend(main_w);
+
+ mvwaddstr(main_w, MW_STATEY, MW_STATEX, " state: ");
+ mvwprintw(main_w, MW_STATEY, MW_STX, "%s", states[state]);
+ wmove(main_w, MW_STATEY+1, 1);
+ whline(main_w, 0, MW_WIDTH-2);
+
+ mvwaddstr(main_w, MW_NUMY, MW_NUMX, " number: ");
+ wmove(main_w, MW_NUMY+1, 1);
+ whline(main_w, 0, MW_WIDTH-2);
+
+ mvwaddstr(main_w, MW_MSGY, MW_MSGX, "message: ");
+
+ wrefresh(main_w);
+
+ curses_ready = 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "Hangup",
+#define HANGUP 0
+ "Dial",
+#define DIAL 1
+ "Refresh",
+#define REFRESH 2
+ "Exit",
+#define EXIT 3
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ return;
+
+ keypad(menu_w, TRUE); /* use special keys */
+
+ /* draw border around the window */
+
+ box(menu_w, 0, 0);
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case TAB:
+ case KEY_DOWN: /* down-move cursor */
+ case ' ':
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case KEY_UP: /* up-move cursor */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ if(mpos)
+ mpos--;
+ else
+ mpos = WMITEMS-1;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case 'R':
+ case 'r':
+ wrefresh(curscr);
+ goto mexit;
+
+ case 'E':
+ case 'e':
+ case 'Q':
+ case 'q':
+ case 'X':
+ case 'x':
+ do_quit(0);
+ goto mexit;
+ break;
+
+ case 'H':
+ case 'h':
+ do_hangup();
+ goto mexit;
+ break;
+
+ case 'D':
+ case 'd':
+ goto mexit;
+ break;
+
+ case CR:
+ case LF: /* exec highlighted option */
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ switch(mpos)
+ {
+ case DIAL:
+ goto mexit;
+ break;
+ case HANGUP:
+ do_hangup();
+ goto mexit;
+ break;
+ case REFRESH:
+ wrefresh(curscr);
+ break;
+ case EXIT:
+ do_quit(0);
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ /* delete the menu window */
+
+ wclear(menu_w);
+ wrefresh(menu_w);
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(main_w);
+ wrefresh(main_w);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/isdn.c b/usr.sbin/i4b/isdnphone/isdn.c
new file mode 100644
index 0000000..34719c7
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/isdn.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - isdn (i4b) handling
+ * ===============================
+ *
+ * $Id: isdn.c,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:05 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ * dialer init
+ *---------------------------------------------------------------------------*/
+int
+init_dial(char *device)
+{
+ int ret;
+
+ if((ret = open(device, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "unable to open %s: %s\n", device, strerror(errno));
+ return(-1);
+ }
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * i4bteld data available handler
+ *---------------------------------------------------------------------------*/
+void
+dial_hdlr(void)
+{
+ char result;
+
+ if((read (dialerfd, &result, 1) < 0))
+ {
+ fatal("read failed: %s", strerror(errno));
+ }
+
+ switch(result)
+ {
+ case RSP_CONN:
+ newstate(ST_ACTIVE);
+ message("connected to remote!");
+ break;
+
+ case RSP_BUSY:
+ message("remote is busy!");
+ break;
+
+ case RSP_HUP:
+ newstate(ST_IDLE);
+ message("disconnected from remote!");
+ break;
+
+ case RSP_NOA:
+ message("no answer from remote!");
+ break;
+
+ default:
+ message("unknown response = 0x%2x!", result);
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * telephone init
+ *---------------------------------------------------------------------------*/
+int
+init_tel(char *device)
+{
+ int ret;
+ int format;
+
+ if(play_fmt == AFMT_MU_LAW)
+ format = CVT_ALAW2ULAW;
+ else
+ format = CVT_NONE;
+
+ if((ret = open(device, O_RDWR)) < 0)
+ fatal("unable to open %s: %s\n", device, strerror(errno));
+
+ if((ioctl(ret, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ fatal("ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * dial number
+ *---------------------------------------------------------------------------*/
+void
+do_dial(char *number)
+{
+ char commandbuffer[80];
+ sprintf(commandbuffer, "D%s", number);
+
+ if((write(dialerfd, commandbuffer, strlen(commandbuffer))) < 0)
+ {
+ fatal("write commandbuffer failed: %s", strerror(errno));
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * hangup
+ *---------------------------------------------------------------------------*/
+void
+do_hangup(void)
+{
+ char commandbuffer[80];
+
+ if(state == ST_IDLE)
+ {
+ message("tried hangup while ST_IDLE");
+ return;
+ }
+
+ sprintf(commandbuffer, "H");
+
+ if((write(dialerfd, commandbuffer, strlen(commandbuffer))) < 0)
+ {
+ fatal("write commandbuffer failed: %s", strerror(errno));
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * i4btel speech data available handler
+ *---------------------------------------------------------------------------*/
+void
+tel_hdlr(void)
+{
+ unsigned char buffer[BCH_MAX_DATALEN];
+ int ret;
+
+ ret = read(telfd, buffer, BCH_MAX_DATALEN);
+
+ if(ret < 0)
+ {
+ fatal("read telfd failed: %s", strerror(errno));
+ }
+
+ debug("tel_hdlr: read %d bytes\n", ret);
+
+ if(ret > 0)
+ {
+ audiowrite(ret, buffer);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * write audio data to ISDN
+ *---------------------------------------------------------------------------*/
+void
+telwrite(int len, unsigned char *buf)
+{
+ if((write(telfd, buf, len)) < 0)
+ {
+ fatal("write tel failed: %s", strerror(errno));
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnphone/isdnphone.8 b/usr.sbin/i4b/isdnphone/isdnphone.8
new file mode 100644
index 0000000..5ba4782
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/isdnphone.8
@@ -0,0 +1,88 @@
+.\"
+.\" Copyright (c) 1999, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Tue Aug 27 16:42:12 2002]
+.\"
+.\"
+.Dd August 27, 2002
+.Dt ISDNPHONE 8
+.Os
+.Sh NAME
+.Nm isdnphone
+.Nd telephone dialing and more for isdn4bsd
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl h
+.Op Fl k Ar string
+.Op Fl n Ar number
+.Op Fl u Ar unit
+.Op Fl v
+.Op Fl w
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is part of the isdn4bsd package and is used to handle dialing and hangup
+for the telephone control interfaces
+.Pa /dev/i4bteld Ns Aq Ar n .
+.Pp
+Options are provided to dial out or hang up using command line parameters
+(for use in scripts) or, if none of those options are specified, bring up
+a curses-based full-screen interface.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Enable debugging message display.
+.It Fl h
+Hang up a possibly open telephone connection on the selected interface.
+.It Fl k
+Send the specified string using the keypad facility information element.
+.It Fl n
+Dial the specified number on the selected interface.
+.It Fl u
+Set the unit number to specify the interface used.
+.It Fl v
+Enable verbose result messages for dialling (-n and -w)
+.It Fl w
+After dialling (using the -n option), wait for the first result message.
+.El
+.Sh FILES
+.Bl -tag -width indent -compact
+.It Pa /dev/i4bteld Ns Aq Ar n
+.El
+.Sh EXAMPLES
+The command:
+.Dl "isdnphone -n 1234"
+.Pp
+dials calls the number 1234 to establish a call on
+.Pa /dev/i4btel0
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/isdnphone/main.c b/usr.sbin/i4b/isdnphone/main.c
new file mode 100644
index 0000000..6b8466a
--- /dev/null
+++ b/usr.sbin/i4b/isdnphone/main.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 1999, 2002 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdnphone - main module
+ * =======================
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Aug 27 16:38:55 2002]
+ *
+ *---------------------------------------------------------------------------*/
+
+#define MAIN
+#include "defs.h"
+
+static void kbd_hdlr(void);
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdnphone - i4b phone program, version %d.%d.%d, compiled %s %s\n",VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdnphone -d -h -k <string> -n <number> -u <unit> -v -w\n");
+ fprintf(stderr, " -d debug\n");
+ fprintf(stderr, " -h hangup\n");
+ fprintf(stderr, " -k string keypad string\n");
+ fprintf(stderr, " -n number dial number\n");
+ fprintf(stderr, " -u unit set unit number\n");
+ fprintf(stderr, " -v be verbose\n");
+ fprintf(stderr, " -w wait for response (with -n)\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ char namebuffer[128];
+ int bschar;
+ int ret;
+ int opt_n = 0;
+ int opt_s = 0;
+ int opt_h = 0;
+ int opt_k = 0;
+ int opt_v = 0;
+ int opt_w = 0;
+ char *number = "";
+ char *subaddr = "";
+
+ numberbuffer[0] = '\0';
+
+ while ((c = getopt(argc, argv, "dhk:n:s:u:w")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ opt_d = 1;
+ break;
+
+ case 'h':
+ opt_h = 1;
+ break;
+
+ case 'k':
+ number = optarg;
+ opt_k = 1;
+ break;
+
+ case 'n':
+ number = optarg;
+ opt_n = 1;
+ break;
+
+ case 's':
+ subaddr = optarg;
+ opt_s = 1;
+ break;
+
+ case 'u':
+ opt_unit = atoi(optarg);
+ if(opt_unit < 0 || opt_unit > 9)
+ usage();
+ break;
+
+ case 'v':
+ opt_v = 1;
+ break;
+
+ case 'w':
+ opt_w = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ sprintf(namebuffer,"%s%d", I4BTELDDEVICE, opt_unit);
+
+ if((dialerfd = init_dial(namebuffer)) == -1)
+ exit(1);
+
+ if(opt_n || opt_h || opt_k)
+ {
+ char commandbuffer[80];
+ int exitval = 0;
+
+ /* commandline operation goes here */
+
+ if(opt_n)
+ {
+ if(opt_s)
+ sprintf(commandbuffer, "D%s*%s", number, subaddr);
+ else
+ sprintf(commandbuffer, "D%s", number);
+
+ }
+ else if(opt_k)
+ {
+ sprintf(commandbuffer, "K%s", number);
+
+ }
+ else if(opt_h)
+ {
+ sprintf(commandbuffer, "H");
+ }
+
+ if((ret = write(dialerfd, commandbuffer, strlen(commandbuffer))) < 0)
+ {
+ fprintf(stderr, "write commandbuffer failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ if(opt_n && opt_w)
+ {
+ char result;
+
+ if((read (dialerfd, &result, 1) < 0))
+ {
+ exitval = 99;
+ if(opt_v)
+ printf("error\n");
+ fprintf(stderr, "error, read failed: %s\n", strerror(errno));
+ }
+ else
+ {
+ switch(result)
+ {
+ case RSP_CONN:
+ exitval = 0;
+ if(opt_v)
+ printf("connected\n");
+ break;
+
+ case RSP_BUSY:
+ exitval = 1;
+ if(opt_v)
+ printf("busy\n");
+ break;
+
+ case RSP_HUP:
+ exitval = 2;
+ if(opt_v)
+ printf("disconnected\n");
+ break;
+
+ case RSP_NOA:
+ exitval = 3;
+ if(opt_v)
+ printf("noanswer\n");
+ break;
+
+ default:
+ exitval = 99;
+ if(opt_v)
+ printf("error\n");
+ fprintf(stderr, "unknown response = 0x%2x!", result);
+ break;
+ }
+ }
+ }
+
+ close(dialerfd);
+
+ exit(exitval);
+ }
+
+ if((audiofd = init_audio(AUDIODEVICE)) == -1)
+ exit(1);
+
+ /* fullscreen operation here */
+
+ init_mainw();
+
+ bschar = erasechar();
+ curx = 0;
+
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+
+ /* go into loop */
+
+ for (;;)
+ {
+ int maxfd = 0;
+ fd_set set;
+ struct timeval timeout;
+
+ FD_ZERO(&set);
+
+ FD_SET(STDIN_FILENO, &set);
+ if(STDIN_FILENO > maxfd)
+ maxfd = STDIN_FILENO;
+
+ FD_SET(dialerfd, &set);
+ if(dialerfd > maxfd)
+ maxfd = dialerfd;
+
+ if(state == ST_ACTIVE)
+ {
+ if(audiofd != -1)
+ {
+ FD_SET(audiofd, &set);
+ if(audiofd > maxfd)
+ maxfd = audiofd;
+ }
+
+ if(telfd != -1)
+ {
+ FD_SET(telfd, &set);
+ if(telfd > maxfd)
+ maxfd = telfd;
+ }
+ }
+
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ wrefresh(main_w);
+
+ /* if no char is available within timeout, do something */
+
+#ifdef NOTDEF
+ ret = select(maxfd+1, &set, NULL, NULL, &timeout);
+#else
+ ret = select(maxfd+1, &set, NULL, NULL, NULL);
+#endif
+
+ if(ret > 0)
+ {
+ if((telfd != -1) && (FD_ISSET(telfd, &set)))
+ {
+ message("select from ISDN");
+ tel_hdlr();
+ }
+ if((audiofd != -1) && (FD_ISSET(audiofd, &set)))
+ {
+ message("select from audio");
+ audio_hdlr();
+ }
+ if(FD_ISSET(dialerfd, &set))
+ {
+ message("select from tel");
+ dial_hdlr();
+ }
+ if(FD_ISSET(STDIN_FILENO, &set))
+ {
+ message("select from kbd");
+ kbd_hdlr();
+ }
+ }
+ }
+ do_quit(0);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * keyboard character available handler
+ *---------------------------------------------------------------------------*/
+static void
+kbd_hdlr(void)
+{
+ int kchar;
+
+ kchar = wgetch(main_w); /* get char */
+
+ switch (kchar)
+ {
+ case CR:
+ case LF:
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ if((state == ST_IDLE) &&
+ (numberbuffer[0] != '\0'))
+ {
+ message("dialing .....");
+ do_dial(&numberbuffer[0]);
+ }
+ else
+ {
+ do_menu();
+ }
+ break;
+
+ case CNTRL_D:
+ if(state == ST_IDLE)
+ {
+ do_quit(0);
+ }
+ else
+ {
+ message("cannot exit while not idle!");
+ beep();
+ }
+
+ break;
+
+ case CNTRL_L: /* refresh */
+ touchwin(curscr);
+ wrefresh(curscr);
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_DC:
+ if (curx == 0)
+ break;
+
+ curx--;
+ mvwaddch(main_w, MW_NUMY, MW_NUX + curx, ' ');
+ numberbuffer[curx] = '\0';
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+
+ if(curx == 0)
+ message(" ");
+
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if(curx > (TELNO_MAX-1))
+ break;
+
+ mvwaddch(main_w, MW_NUMY, MW_NUX + curx, kchar);
+
+ numberbuffer[curx] = kchar;
+
+ curx++;
+
+ numberbuffer[curx] = '\0';
+
+ message("press ENTER to dial number .....");
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * exit program
+ *---------------------------------------------------------------------------*/
+void
+do_quit(int exitval)
+{
+ close(dialerfd);
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * fatal error exit
+ *---------------------------------------------------------------------------*/
+void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ do_hangup(); /* failsafe */
+
+ if(curses_ready)
+ {
+ close(dialerfd);
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ }
+
+ fprintf(stderr, "\nFatal error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * message printing
+ *---------------------------------------------------------------------------*/
+void
+message(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(curses_ready)
+ {
+ int i;
+ char sbuf[MW_WIDTH];
+
+ wmove(main_w, MW_MSGY, MW_MSX);
+ vsnprintf(sbuf, MW_WIDTH-MW_MSX-1, fmt, ap);
+ waddstr(main_w, sbuf);
+ for(i=strlen(sbuf);i < MW_WIDTH-MW_MSX-2; i++)
+ waddch(main_w, ' ');
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+ wrefresh(main_w);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ }
+
+ va_end(ap);
+}
+
+/*---------------------------------------------------------------------------*
+ * message printing
+ *---------------------------------------------------------------------------*/
+void
+debug(char *fmt, ...)
+{
+ va_list ap;
+
+ if(opt_d == 0)
+ return;
+
+ va_start(ap, fmt);
+
+ vwprintw(dbg_w, fmt, ap);
+ wrefresh(dbg_w);
+
+ va_end(ap);
+}
+
+/*---------------------------------------------------------------------------*
+ * go to new state
+ *---------------------------------------------------------------------------*/
+void
+newstate(int newstate)
+{
+ int i;
+
+ if(newstate < 0 || newstate > ST_MAX)
+ {
+ message("newstate %d undefined!", newstate);
+ return;
+ }
+
+ state = newstate;
+
+ if(newstate == ST_ACTIVE)
+ {
+ char namebuffer[128];
+
+ sprintf(namebuffer,"%s%d", I4BTELDEVICE, opt_unit);
+ telfd = init_tel(namebuffer);
+ }
+
+ if(newstate == ST_IDLE)
+ {
+ close(telfd);
+ telfd = -1;
+ }
+
+ wmove(main_w, MW_STATEY, MW_STX);
+ waddstr(main_w, states[newstate]);
+
+ for(i=strlen(states[newstate]);i < MW_WIDTH-MW_STX-2; i++)
+ waddch(main_w, ' ');
+
+ wmove(main_w, MW_NUMY, MW_NUX + curx);
+ wrefresh(main_w);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/Makefile b/usr.sbin/i4b/isdntel/Makefile
new file mode 100644
index 0000000..e3aff09
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= isdntel
+MAN= isdntel.8
+SRCS= main.c display.c files.c alias.c
+
+DPADD= ${LIBNCURSES}
+LDADD= -lncurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdntel/alias.c b/usr.sbin/i4b/isdntel/alias.c
new file mode 100644
index 0000000..0f4dae7
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/alias.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: alias.c,v 1.9 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:37 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+#include "alias.h"
+
+static struct alias *firsta = NULL;
+
+#define MAXBUFSZ 256
+
+/*---------------------------------------------------------------------------*
+ * read in and init aliases
+ *---------------------------------------------------------------------------*/
+void
+init_alias(char *filename)
+{
+ FILE *fp;
+ unsigned char buffer[MAXBUFSZ + 1];
+ unsigned char number[MAXBUFSZ + 1];
+ unsigned char name[MAXBUFSZ + 1];
+ unsigned char *s, *d;
+ struct alias *newa = NULL;
+ struct alias *lasta = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ return;
+
+ while((fgets(buffer, MAXBUFSZ, fp)) != NULL)
+ {
+ if(buffer[0] == '#' || buffer[0] == ' ' ||
+ buffer[0] == '\t' || buffer[0] == '\n')
+ {
+ continue;
+ }
+
+ s = buffer;
+ d = number;
+
+ while(*s && (isdigit(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ while(*s && (isspace(*s)))
+ s++;
+
+ d = name;
+
+ while(*s && (isprint(*s)))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((strlen(number) > 1) && (strlen(name) > 1))
+ {
+ if((newa = (struct alias *) malloc(sizeof(struct alias))) == NULL)
+ fatal("malloc failed for struct alias");
+ if((newa->number = (char *) malloc(strlen(number)+1)) == NULL)
+ fatal("malloc failed for number alias");
+ if((newa->name = (char *) malloc(strlen(name)+1)) == NULL)
+ fatal("malloc failed for name alias");
+ strcpy(newa->name, name);
+ strcpy(newa->number, number);
+ newa->next = NULL;
+
+ if(firsta == NULL)
+ {
+ firsta = newa;
+ }
+ else
+ {
+ lasta->next = newa;
+ }
+ lasta = newa;
+ }
+ }
+ fclose(fp);
+}
+
+/*---------------------------------------------------------------------------*
+ * read in and init aliases
+ *---------------------------------------------------------------------------*/
+char *
+get_alias(char *number)
+{
+ struct alias *ca = NULL;
+
+ if(firsta == NULL)
+ return(NULL);
+
+ ca = firsta;
+
+ for(;;)
+ {
+ if(strlen(number) == strlen(ca->number))
+ {
+ if(!(strcmp(number, ca->number)))
+ return(ca->name);
+ }
+ if(ca->next == NULL)
+ break;
+ ca = ca->next;
+ }
+ return(NULL);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/alias.h b/usr.sbin/i4b/isdntel/alias.h
new file mode 100644
index 0000000..813d5bd
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/alias.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdn4bsd common alias file handling header
+ * ==========================================
+ *
+ * $Id: alias.h,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:44 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#ifndef _ALIAS_H_
+#define _ALIAS_H_
+
+#define ALIASFILE "/etc/isdn/isdntel.alias"
+
+struct alias {
+ char *number; /* telephone number string */
+ char *name; /* name string */
+ struct alias *next; /* ptr to next alias */
+};
+
+#endif /* _ALIAS_H_ */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/defs.h b/usr.sbin/i4b/isdntel/defs.h
new file mode 100644
index 0000000..9b5fd69
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/defs.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering support
+ * ==============================================
+ *
+ * $Id: defs.h,v 1.10 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:53:50 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include <ncurses.h>
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/time.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#endif
+#include <sys/param.h>
+
+#include <machine/i4b_ioctl.h>
+
+#define GOOD 0
+#define ERROR (-1)
+#define WARNING (-2)
+
+#define SPOOLDIR "/var/isdn"
+#define PLAYCMD "cat %s | g711conv -a >/dev/audio"
+
+/* reread timeout in seconds */
+
+#define REREADTIMEOUT 60
+
+/* window dimensions */
+
+#define START_O 3 /* main window start */
+
+#define DAT_POS 0
+#define TIM_POS (DAT_POS+10)
+#define DST_POS (TIM_POS+8)
+#define SRC_POS (DST_POS+17)
+#define ALI_POS (SRC_POS+17)
+#define SEC_POS (ALI_POS+21)
+#define LAST_POS (SEC_POS+5)
+
+/* fullscreen mode menu window */
+
+#define WMITEMS 5 /* no of items */
+#define WMENU_LEN 18 /* window width */
+#define WMENU_HGT (WMITEMS+4) /* window height */
+#define WMENU_TITLE "Command"
+#define WMENU_POSLN 8 /* window position: lines */
+#define WMENU_POSCO 20 /* window position: columns */
+
+#define CR 0x0d
+#define LF 0x0a
+#define TAB 0x09
+#define CNTRL_D 0x04
+#define CNTRL_L 0x0c
+
+struct onefile {
+ char *fname; /* filename */
+ char *date;
+ char *time;
+ char *srcnumber;
+ char *dstnumber;
+ char *seconds;
+ char *alias;
+ int len;
+ struct onefile *next; /* ptr to next entry */
+ struct onefile *prev; /* prt to previous entry */
+};
+
+#ifdef MAIN
+
+int curses_ready = 0; /* flag, curses display is initialized */
+
+struct onefile *cur_file = NULL;/* the CURRENT filename */
+struct onefile *first = NULL; /* init dir-list head-ptr */
+struct onefile *last = NULL; /* init dir-list tail-ptr */
+
+WINDOW *main_w; /* curses main window pointer */
+
+int nofiles = 0;
+int cur_pos = 0;
+
+char *spooldir = SPOOLDIR;
+char *playstring = PLAYCMD;
+
+#else
+
+extern int curses_ready;
+
+extern struct onefile *cur_file;
+extern struct onefile *first;
+extern struct onefile *last;
+
+extern WINDOW *main_w;
+
+extern int nofiles;
+extern int cur_pos;
+
+extern char *spooldir;
+extern char *playstring;
+
+#endif
+
+extern void init_alias( char *filename );
+extern void init_files( int inipos );
+extern void init_screen ( void );
+extern void do_menu ( void );
+extern int fill_list( void );
+extern char *get_alias( char *number );
+extern int main ( int argc, char **argv );
+extern void do_quit ( int exitval );
+extern void fatal ( char *fmt, ... );
+extern void error ( char *fmt, ... );
+extern void play ( struct onefile * );
+extern void delete ( struct onefile * );
+extern void reread( void );
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/display.c b/usr.sbin/i4b/isdntel/display.c
new file mode 100644
index 0000000..53e6637
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/display.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: display.c,v 1.9 2000/07/19 08:56:24 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Jul 19 10:08:06 2000]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+static char *helpstr = "Enter Control-D to exit program or RETURN for command window";
+
+/*---------------------------------------------------------------------------*
+ * init curses fullscreen display
+ *---------------------------------------------------------------------------*/
+void
+init_screen(void)
+{
+ char buffer[512];
+
+ initscr(); /* curses init */
+
+ curses_ready = 1;
+
+ if((COLS < 80) || (LINES < 24))
+ fatal("ERROR, minimal screensize must be 80x24, is %dx%d, terminating!", COLS, LINES);
+
+
+ if((main_w = newwin(LINES-START_O-2, COLS, START_O, 0)) == NULL)
+ fatal("ERROR, curses init main window, terminating!");
+
+ raw(); /* raw input */
+ noecho(); /* do not echo input */
+ keypad(stdscr, TRUE); /* use special keys */
+ keypad(main_w, TRUE); /* use special keys */
+ scrollok(main_w, TRUE);
+
+ sprintf(buffer, " isdntel %d.%d.%d ", VERSION, REL, STEP);
+
+ move(0, 0);
+ standout();
+ hline(ACS_HLINE, 5);
+ move(0, 5);
+ addstr(buffer);
+ move(0, 5 + strlen(buffer));
+ hline(ACS_HLINE, 256);
+ standend();
+
+ move(1, 0);
+ addstr("Date Time Called Party Calling Party Alias Length");
+ /* 31.12.96 16:45:12 1234567890123456 1234567890123456 12345678901234567890 123456 */
+
+ move(2, 0);
+ hline(ACS_HLINE, 256);
+
+ move(LINES-2, 0);
+ hline(ACS_HLINE, 256);
+
+ mvaddstr(LINES-1, (COLS / 2) - (strlen(helpstr) / 2), helpstr);
+
+ refresh();
+
+ wrefresh(main_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * curses menu for fullscreen command mode
+ *---------------------------------------------------------------------------*/
+void
+do_menu(void)
+{
+ static char *menu[WMITEMS] =
+ {
+ "Play File",
+#define PLAY 0
+ "Delete File",
+#define DELETE 1
+ "Re-Read Spool",
+#define REREAD 2
+ "Refresh Screen",
+#define REFRESH 3
+ "Exit Program",
+#define EXIT 4
+ };
+
+ WINDOW *menu_w;
+ int c;
+ int mpos;
+
+ /* create a new window in the lower screen area */
+
+ if((menu_w = newwin(WMENU_HGT, WMENU_LEN, WMENU_POSLN, WMENU_POSCO )) == NULL)
+ return;
+
+ keypad(menu_w, TRUE); /* use special keys */
+
+ /* draw border around the window */
+
+ box(menu_w, 0, 0);
+
+ /* add a title */
+
+ wstandout(menu_w);
+ mvwaddstr(menu_w, 0, (WMENU_LEN / 2) - (strlen(WMENU_TITLE) / 2), WMENU_TITLE);
+ wstandend(menu_w);
+
+ /* fill the window with the menu options */
+
+ for(mpos=0; mpos <= (WMITEMS-1); mpos++)
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+
+ /* highlight the first menu option */
+
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+
+ /* input loop */
+
+ for(;;)
+ {
+ wrefresh(menu_w);
+
+ c = wgetch(menu_w);
+
+ switch(c)
+ {
+ case TAB:
+ case KEY_DOWN: /* down-move cursor */
+ case ' ':
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ mpos++;
+ if(mpos >= WMITEMS)
+ mpos = 0;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case KEY_UP: /* up-move cursor */
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ if(mpos)
+ mpos--;
+ else
+ mpos = WMITEMS-1;
+ wstandout(menu_w);
+ mvwaddstr(menu_w, mpos + 2, 2, menu[mpos]);
+ wstandend(menu_w);
+ break;
+
+ case 'R':
+ case 'r':
+ wrefresh(curscr);
+ goto mexit;
+
+ case 'E':
+ case 'e':
+ case 'Q':
+ case 'q':
+ case 'X':
+ case 'x':
+ do_quit(0);
+ goto mexit;
+ break;
+
+ case 'P':
+ case 'p':
+ play(cur_file);
+ goto mexit;
+ break;
+
+ case 'D':
+ case 'd':
+ delete(cur_file);
+ goto mexit;
+ break;
+
+ case CR:
+ case LF: /* exec highlighted option */
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ switch(mpos)
+ {
+ case PLAY:
+ play(cur_file);
+ goto mexit;
+ break;
+ case DELETE:
+ delete(cur_file);
+ goto mexit;
+ break;
+ case REREAD:
+ reread();
+ goto mexit;
+ break;
+ case REFRESH:
+ wrefresh(curscr);
+ break;
+ case EXIT:
+ do_quit(0);
+ break;
+ }
+ goto mexit;
+ break;
+
+ default:
+ goto mexit;
+ break;
+ }
+ }
+
+mexit:
+ /* delete the menu window */
+
+ delwin(menu_w);
+
+ /* re-display the original lower window contents */
+
+ touchwin(main_w);
+ wrefresh(main_w);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntel/files.c b/usr.sbin/i4b/isdntel/files.c
new file mode 100644
index 0000000..bf05b42
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/files.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: files.c,v 1.8 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:54:06 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#include "defs.h"
+
+/*---------------------------------------------------------------------------*
+ * create a doubly linked list in sorted order, return pointer to new
+ * first element of list
+ *---------------------------------------------------------------------------*/
+struct onefile *store
+ (register struct onefile *new, /* new entry to store into list */
+ register struct onefile *top) /* current first entry in list */
+{
+ register struct onefile *old, *p;
+
+ if (last == NULL) /* enter very first element ? */
+ {
+ new->next = NULL;
+ new->prev = NULL;
+ last = new; /* init last */
+ return (new); /* return new first */
+ }
+ p = top; /* p = old first element */
+ old = NULL;
+ while (p)
+ {
+ if ((strcmp(p->fname, new->fname)) < 0) /* current less new ? */
+ {
+ old = p;
+ p = p->next;
+ }
+ else
+ { /* current >= new */
+
+ if (p->prev)
+ {
+ p->prev->next = new;
+ new->next = p;
+ new->prev = p->prev;
+ p->prev = new;
+ return (top);
+ }
+ new->next = p;
+ new->prev = NULL;
+ p->prev = new;
+ return (new);
+ }
+ }
+ old->next = new;
+ new->next = NULL;
+ new->prev = old;
+ last = new;
+ return (first);
+}
+
+/*---------------------------------------------------------------------------*
+ * read current directory and build up a doubly linked sorted list
+ *---------------------------------------------------------------------------*/
+int
+fill_list(void)
+{
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+ register struct dirent *dp;
+#else
+ register struct direct *dp;
+#endif
+ register struct onefile *new_entry;
+ register DIR *dirp;
+ int flcnt = 0;
+ char tmp[80];
+ char *s, *d;
+
+ if ((dirp = opendir(spooldir)) == NULL)
+ fatal("cannot open spooldirectory %s!\n", spooldir);
+
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
+ {
+ if(!isdigit(*(dp->d_name)))
+ continue;
+
+ if ((new_entry = (struct onefile *) malloc(sizeof(struct onefile))) == NULL)
+ {
+ fatal("files.c, fill_list(): structure onefile malloc failed");
+ }
+
+ /* alloc filename memory and copy name into it */
+
+ if ((new_entry->fname = (char *) malloc(strlen(dp->d_name) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc filename string memory failed");
+ }
+
+ strcpy(new_entry->fname, dp->d_name);
+
+ /* fill in remaining fields from filename */
+
+ tmp[0] = dp->d_name[4]; /* day msb */
+ tmp[1] = dp->d_name[5]; /* day lsb */
+ tmp[2] = '.';
+ tmp[3] = dp->d_name[2]; /* month msb */
+ tmp[4] = dp->d_name[3]; /* month lsb */
+ tmp[5] = '.';
+ tmp[6] = dp->d_name[0]; /* year msb */
+ tmp[7] = dp->d_name[1]; /* year lsb */
+ tmp[8] = '\0';
+
+ if((new_entry->date = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc date string memory failed");
+ }
+
+ strcpy(new_entry->date, tmp);
+
+ tmp[0] = dp->d_name[6]; /* hour msb */
+ tmp[1] = dp->d_name[7]; /* hour lsb */
+ tmp[2] = ':';
+ tmp[3] = dp->d_name[8]; /* minute msb */
+ tmp[4] = dp->d_name[9]; /* minute lsb */
+ tmp[5] = ':';
+ tmp[6] = dp->d_name[10]; /* second msb */
+ tmp[7] = dp->d_name[11]; /* second lsb */
+ tmp[8] = '\0';
+
+ if((new_entry->time = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc time string memory failed");
+ }
+
+ strcpy(new_entry->time, tmp);
+
+ /* destination number */
+
+ s = &dp->d_name[13];
+ d = &tmp[0];
+
+ while(*s && (*s != '-'))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((new_entry->dstnumber = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc dstnumber string memory failed");
+ }
+
+ strcpy(new_entry->dstnumber, tmp);
+
+ /* source number */
+
+ s++;
+ d = &tmp[0];
+
+ while(*s && (*s != '-'))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((new_entry->srcnumber = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc srcnumber string memory failed");
+ }
+
+ strcpy(new_entry->srcnumber, tmp);
+
+ /* length in seconds */
+
+ s++;
+ d = &tmp[0];
+
+ while(*s && (*s != '-'))
+ *d++ = *s++;
+
+ *d = '\0';
+
+ if((new_entry->seconds = (char *) malloc(strlen(tmp) + 1)) == NULL)
+ {
+ fatal("files.c, fill_list(): malloc seconds string memory failed");
+ }
+
+ strcpy(new_entry->seconds, tmp);
+
+ /* search for alias and add if found */
+
+ new_entry->alias = get_alias(new_entry->srcnumber);
+
+ /* sort entry into linked list */
+
+ first = store(new_entry, first);
+
+ flcnt++; /* increment file count */
+ }
+ closedir(dirp); /* close current dir */
+ return(flcnt); /* ok return */
+}
+
+/*---------------------------------------------------------------------------*
+ * free the current malloc'ed list
+ *---------------------------------------------------------------------------*/
+void
+free_list(void)
+{
+ register struct onefile *dir;
+ register struct onefile *tmp;
+
+ dir = first; /* start of linked list */
+
+ while (dir) /* free all */
+ {
+ tmp = dir->next; /* save ptr to next entry */
+ free(dir->fname); /* free filename space */
+ free(dir->date);
+ free(dir->time);
+ free(dir->srcnumber);
+ free(dir->dstnumber);
+ free(dir->seconds);
+ free(dir); /* free struct space */
+ dir = tmp; /* ptr = ptr to next entry */
+ }
+ first = NULL; /* first ptr = NULL */
+ last = NULL; /* last ptr = NULL */
+}
+
+/*---------------------------------------------------------------------------*
+ * delete a file
+ *---------------------------------------------------------------------------*/
+void
+delete(struct onefile *this)
+{
+ char buffer[MAXPATHLEN];
+
+ if(this == NULL)
+ return;
+
+ strlcpy(buffer, this->fname, sizeof(buffer));
+
+ unlink(buffer);
+
+ free_list();
+
+ wclear(main_w);
+
+ init_files(cur_pos);
+}
+
+/*---------------------------------------------------------------------------*
+ * reread the spool directory
+ *---------------------------------------------------------------------------*/
+void
+reread(void)
+{
+ free_list();
+
+ wclear(main_w);
+
+ init_files(cur_pos);
+}
+
+/*---------------------------------------------------------------------------*
+ * play a file
+ *---------------------------------------------------------------------------*/
+void
+play(struct onefile *this)
+{
+ char buffer[MAXPATHLEN];
+
+ if(this == NULL)
+ return;
+
+ snprintf(buffer, sizeof(buffer), playstring, this->fname);
+
+ system(buffer);
+}
+
+/*---------------------------------- EOF -------------------------------------*/
diff --git a/usr.sbin/i4b/isdntel/isdntel.8 b/usr.sbin/i4b/isdntel/isdntel.8
new file mode 100644
index 0000000..7e1ed50
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/isdntel.8
@@ -0,0 +1,97 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" last edit-date: [Mon Dec 13 23:05:59 1999]
+.\"
+.\" $FreeBSD$
+.\"
+.\" $Id: isdntel.8,v 1.9 1999/12/13 22:11:55 hm Exp $
+.\"
+.Dd July 11, 1998
+.Dt ISDNTEL 8
+.Os
+.Sh NAME
+.Nm isdntel
+.Nd isdn4bsd telephone answering management utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar aliasfile
+.Op Fl d Ar spooldir
+.Op Fl p Ar playcommand
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to provide an "answering machine" functionality for incoming
+telephone voice messages.
+.Pp
+The following options are supported:
+.Bl -tag -width Ds
+.It Fl a
+Use
+.Ar aliasfile
+as the pathname for an aliasfile containing aliases for phone numbers. The
+default path is
+.Em /etc/isdn/isdntel.alias .
+The format of an alias entry is the number string followed by one or more
+spaces or tabs. The rest of the line is taken as the alias string. Comments
+are introduced by a leading blank, tab or "#" character.
+.It Fl d
+Use
+.Ar spooldir
+as the directory where the incoming voice messages are stored by the
+"answ" script called by
+.Xr isdnd 8 .
+This defaults to the directory
+.Em /var/isdn .
+The format of a voice message filename is:
+.Pp
+.Dl YYMMDDhhmmss-dest_number-source_number-length_in_secs
+.It Fl p
+Use
+.Ar playcommand
+as the command string to execute for playing a voice message to some audio
+output facility. The characters
+.Em %s
+are replaced by the currently selected filename. The default string is
+.Dq Li cat %s \&| alaw2ulaw >/dev/audio .
+.It Fl t
+The value for
+.Ar timeout
+specifies the time in seconds the program rereads the spool directory
+when there is no keyboard activity.
+.El
+.Pp
+The screen output should be obvious. If in doubt, consult the source.
+.Sh SEE ALSO
+.Xr i4btel 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh BUGS
+Still two or more left.
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdntel/main.c b/usr.sbin/i4b/isdntel/main.c
new file mode 100644
index 0000000..6bea966
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/main.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntel - isdn4bsd telephone answering machine support
+ * ======================================================
+ *
+ * $Id: main.c,v 1.12 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:54:26 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#define MAIN
+#include <locale.h>
+#include <unistd.h>
+#include "defs.h"
+#include "alias.h"
+
+static void usage( void );
+
+static int top_dis = 0;
+static int bot_dis = 0;
+static int cur_pos_scr = 0;
+
+static void makecurrent(int cur_pos, struct onefile *cur_file, int cold);
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int i;
+ int kchar;
+
+ char *spooldir = SPOOLDIR;
+ char *playstring = PLAYCMD;
+ char *aliasfile = ALIASFILE;
+ int rrtimeout = REREADTIMEOUT;
+
+ setlocale( LC_ALL, "");
+
+ while ((i = getopt(argc, argv, "a:d:p:t:")) != -1)
+ {
+ switch (i)
+ {
+ case 'a':
+ aliasfile = optarg;
+ break;
+
+ case 'd':
+ spooldir = optarg;
+ break;
+
+ case 'p':
+ playstring = optarg;
+ break;
+
+ case 't':
+ if(isdigit(*optarg))
+ {
+ rrtimeout = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ usage();
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(rrtimeout < 10)
+ rrtimeout = 10;
+
+ if((chdir(spooldir)) != 0)
+ fatal("cannot change directory to spooldir %s!", spooldir);
+
+ init_alias(aliasfile);
+
+ init_screen();
+
+ init_files(0);
+
+ /* go into loop */
+
+ for (;;)
+ {
+ fd_set set;
+ struct timeval timeout;
+
+ FD_ZERO(&set);
+ FD_SET(STDIN_FILENO, &set);
+ timeout.tv_sec = rrtimeout;
+ timeout.tv_usec = 0;
+
+ /* if no char is available within timeout, reread spool */
+
+ if((select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout)) <= 0)
+ {
+ reread();
+ continue;
+ }
+
+ kchar = wgetch(main_w); /* get char */
+
+ switch (kchar)
+ {
+ case CR:
+ case LF:
+#ifdef KEY_ENTER
+ case KEY_ENTER:
+#endif
+ do_menu();
+ break;
+
+ case KEY_UP: /* up-move cursor */
+ if(cur_file && cur_file->prev)
+ {
+ cur_file = cur_file->prev;
+ cur_pos--;
+ }
+ break;
+
+
+ case TAB:
+ case KEY_DOWN: /* down-move cursor */
+ if(cur_file && cur_file->next)
+ {
+ cur_file = cur_file->next;
+ cur_pos++;
+ }
+ break;
+
+ case KEY_HOME: /* move cursor to first dir */
+ break;
+
+ case KEY_LL: /* move cursor to last file */
+ break;
+
+ case CNTRL_D:
+ do_quit(0);
+ break;
+
+ case CNTRL_L: /* refresh */
+ touchwin(curscr);
+ wrefresh(curscr);
+ break;
+
+ }
+ makecurrent(cur_pos, cur_file, 0);
+ }
+
+ do_quit(0);
+
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle horizontal selection bar movement
+ *---------------------------------------------------------------------------*/
+static void
+makecurrent(int cur_pos, struct onefile *cur_file, int cold)
+{
+ static int lastpos;
+ static struct onefile *lastfile;
+ char buffer[256];
+
+ /* un-higlight current horizontal bar */
+
+ if(!cold && lastfile && cur_file)
+ {
+ sprintf(buffer, "%s %s %-16s %-16s %-20s %-6s%*s",
+ lastfile->date, lastfile->time,
+ lastfile->dstnumber, lastfile->srcnumber,
+ lastfile->alias == NULL ? "-/-" : lastfile->alias,
+ lastfile->seconds,
+ COLS - LAST_POS - 2, "");
+
+ wattroff(main_w, A_REVERSE);
+ mvwprintw(main_w, lastpos, 0, "%s", buffer);
+ wattroff(main_w, A_REVERSE);
+ }
+
+ if(cur_file == NULL)
+ {
+ lastpos = cur_pos_scr;
+ lastfile = cur_file;
+ return;
+ }
+
+ /* have to scroll up or down ? */
+
+ if(cur_pos >= bot_dis)
+ {
+ /* scroll up */
+
+ wscrl(main_w, 1);
+
+ bot_dis++;
+ top_dis++;
+ cur_pos_scr = LINES-START_O-3;
+ }
+ else if(cur_pos < top_dis)
+ {
+ /* scroll down */
+
+ wscrl(main_w, -1);
+
+ bot_dis--;
+ top_dis--;
+ cur_pos_scr = 0;
+ }
+ else
+ {
+ cur_pos_scr = cur_pos - top_dis;
+ }
+
+ sprintf(buffer, "%s %s %-16s %-16s %-20s %-6s%*s",
+ cur_file->date, cur_file->time,
+ cur_file->dstnumber, cur_file->srcnumber,
+ cur_file->alias == NULL ? "-/-" : cur_file->alias,
+ cur_file->seconds,
+ COLS - LAST_POS - 2, "");
+
+ wattron(main_w, A_REVERSE);
+ mvwprintw(main_w, cur_pos_scr, 0, "%s", buffer);
+ wattroff(main_w, A_REVERSE);
+
+ lastpos = cur_pos_scr;
+ lastfile = cur_file;
+
+ wrefresh(main_w);
+}
+
+/*---------------------------------------------------------------------------*
+ * exit program
+ *---------------------------------------------------------------------------*/
+void
+do_quit(int exitval)
+{
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(exitval);
+}
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdntel - isdn telephone answering management support utility (version %d.%d.%d)\n", VERSION, REL, STEP);
+ fprintf(stderr, " usage: isdntel -a <filename> -d <directory> -p <command> -t <timeout>\n");
+ fprintf(stderr, " -a <filename> use filename as alias file\n");
+ fprintf(stderr, " -d <directory> use directory as spool directory\n");
+ fprintf(stderr, " -p <command> specify commandline for play command\n");
+ fprintf(stderr, " -t <timeout> spool directory reread timeout in seconds\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * fatal error exit
+ *---------------------------------------------------------------------------*/
+void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(curses_ready)
+ {
+ move(LINES-1, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ }
+
+ fprintf(stderr, "\nFatal error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n\n");
+
+ va_end(ap);
+
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * error printing
+ *---------------------------------------------------------------------------*/
+void
+error(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if(curses_ready)
+ {
+ wprintw(main_w, "ERROR: ");
+ vwprintw(main_w, fmt, ap);
+ wprintw(main_w, "\n");
+ wrefresh(main_w);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ }
+
+ va_end(ap);
+}
+
+/*---------------------------------------------------------------------------*
+ * read files and fill display
+ *---------------------------------------------------------------------------*/
+void
+init_files(int inipos)
+{
+ int i;
+
+ nofiles = fill_list();
+
+ top_dis = 0;
+ bot_dis = 0;
+
+ cur_file = first;
+
+ cur_pos = 0;
+ cur_pos_scr = 0;
+
+ if(nofiles == 0)
+ return;
+
+ for(i=0; (i < nofiles) && (i < (LINES-START_O-2)); i++)
+ {
+ mvwprintw(main_w, i, 0, "%s %s", cur_file->date, cur_file->time);
+ mvwprintw(main_w, i, DST_POS, "%s", cur_file->dstnumber);
+ mvwprintw(main_w, i, SRC_POS, "%s", cur_file->srcnumber);
+ mvwprintw(main_w, i, ALI_POS,"%s", cur_file->alias == NULL ? "-/-" : cur_file->alias);
+ mvwprintw(main_w, i, SEC_POS,"%s", cur_file->seconds);
+
+ bot_dis++;
+
+ if((cur_file = cur_file->next) == NULL)
+ break;
+ }
+
+ cur_file = first;
+
+ if(inipos)
+ {
+ for(i=0; i < inipos; i++)
+ {
+ if(cur_file->next != NULL)
+ cur_file = cur_file->next;
+ else
+ break;
+ }
+ }
+ makecurrent(cur_pos, cur_file, 1);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntelctl/Makefile b/usr.sbin/i4b/isdntelctl/Makefile
new file mode 100644
index 0000000..41b771e
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= isdntelctl
+MAN= isdntelctl.8
+SRCS= main.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdntelctl/isdntelctl.8 b/usr.sbin/i4b/isdntelctl/isdntelctl.8
new file mode 100644
index 0000000..2078825
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/isdntelctl.8
@@ -0,0 +1,98 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdntelctl.8,v 1.9 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:06:45 1999]
+.\"
+.Dd April 21, 1999
+.Dt ISDNTELCTL 8
+.Os
+.Sh NAME
+.Nm isdntelctl
+.Nd control isdn4bsd telephone sound format conversion
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl g
+.Op Fl u Ar unit
+.Op Fl A
+.Op Fl U
+.Op Fl N
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is part of the isdn4bsd package and is used to configure the sound format
+conversion facilities of the /dev/i4btel interfaces.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Clear the telephone input queue.
+.It Fl g
+Get the sound format currently in use.
+.It Fl u
+Set the /dev/i4btel unit number. The default value is zero to access
+device /dev/i4btel0.
+.It Fl A
+Do A-law (ISDN line) -> u-law (userland) conversion.
+.It Fl U
+Do u-law (ISDN line) -> A-law (userland) conversion.
+.It Fl N
+Set sound conversion to do no format conversion.
+.El
+.Pp
+The telephony data stream comes out of the line in a bit-reversed format,
+so the
+.Xr i4btel 4
+driver does the bit-reversion process in any case.
+.Pp
+Additionally, the user can specify to do A-law to u-law, u-law to A-law
+or no conversion at all in the i4btel driver by using the
+.Nm
+utility.
+.Sh FILES
+/dev/i4btel<n>
+.Sh STANDARDS
+A-Law and u-Law are specified in ITU Recommendation G.711.
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdntelctl -g
+.Ed
+.Pp
+displays the currently used sound format for device /dev/i4btel0.
+.Sh SEE ALSO
+.Xr g711conv 1 ,
+.Xr i4btel 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdntelctl/main.c b/usr.sbin/i4b/isdntelctl/main.c
new file mode 100644
index 0000000..3d75663
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/main.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * isdntelctl - i4b set telephone interface options
+ * ------------------------------------------------
+ *
+ * $Id: main.c,v 1.12 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:54:50 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <paths.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_tel_ioctl.h>
+
+static void usage ( void );
+
+#define I4BTELDEVICE "/dev/i4btel"
+
+int opt_get = 0;
+int opt_unit = 0;
+int opt_U = 0;
+int opt_A = 0;
+int opt_C = 0;
+int opt_N = 0;
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ int ret;
+ int telfd;
+ char namebuffer[128];
+
+ while ((c = getopt(argc, argv, "cgu:AUN")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ opt_C = 1;
+ break;
+
+ case 'g':
+ opt_get = 1;
+ break;
+
+ case 'u':
+ opt_unit = atoi(optarg);
+ if(opt_unit < 0 || opt_unit > 9)
+ usage();
+ break;
+
+ case 'A':
+ opt_A = 1;
+ break;
+
+ case 'U':
+ opt_U = 1;
+ break;
+
+ case 'N':
+ opt_N = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(opt_get == 0 && opt_N == 0 && opt_U == 0 && opt_A == 0 && opt_C == 0)
+ {
+ opt_get = 1;
+ }
+
+ if((opt_get + opt_N + opt_U + opt_A + opt_C) > 1)
+ {
+ usage();
+ }
+
+ sprintf(namebuffer,"%s%d", I4BTELDEVICE, opt_unit);
+
+ if((telfd = open(namebuffer, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "isdntelctl: cannot open %s: %s\n", namebuffer, strerror(errno));
+ exit(1);
+ }
+
+ if(opt_get)
+ {
+ int format;
+
+ if((ret = ioctl(telfd, I4B_TEL_GETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_GETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ if(format == CVT_NONE)
+ {
+ printf("device %s does not do A-law/u-law format conversion\n", namebuffer);
+ }
+ else if(format == CVT_ALAW2ULAW)
+ {
+ printf("device %s does ISDN: A-law -> user: u-law format conversion\n", namebuffer);
+ }
+ else if(format == CVT_ULAW2ALAW)
+ {
+ printf("device %s does ISDN: u-law -> user: A-law format conversion\n", namebuffer);
+ }
+ else
+ {
+ printf("ERROR, device %s uses unknown format %d!\n", namebuffer, format);
+ }
+ exit(0);
+ }
+
+ if(opt_A)
+ {
+ int format = CVT_ALAW2ULAW;
+
+ if((ret = ioctl(telfd, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if(opt_U)
+ {
+ int format = CVT_ULAW2ALAW;
+
+ if((ret = ioctl(telfd, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ if(opt_N)
+ {
+ int format = CVT_NONE;
+
+ if((ret = ioctl(telfd, I4B_TEL_SETAUDIOFMT, &format)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_SETAUDIOFMT failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ if(opt_C)
+ {
+ int dummy;
+ if((ret = ioctl(telfd, I4B_TEL_EMPTYINPUTQUEUE, &dummy)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_TEL_EMPTYINPUTQUEUE failed: %s", strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdntelctl - %si4btel control, version %d.%d.%d (%s %s)\n", _PATH_DEV, VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdntelctl -c -g -u <unit> -A -N -U\n");
+ fprintf(stderr, " -c clear input queue\n");
+ fprintf(stderr, " -g get current settings\n");
+ fprintf(stderr, " -u unit specify unit number\n");
+ fprintf(stderr, " -A set conversion ISDN: A-law -> user: u-law\n");
+ fprintf(stderr, " -U set conversion ISDN: u-law -> user: A-law\n");
+ fprintf(stderr, " -N set conversion to no A-law/u-law conversion\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntest/Makefile b/usr.sbin/i4b/isdntest/Makefile
new file mode 100644
index 0000000..88b491a
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= isdntest
+MAN= isdntest.8
+SRCS= main.c
+
+install:
+ @${ECHO} "isdntest is not installed automatically"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdntest/isdntest.8 b/usr.sbin/i4b/isdntest/isdntest.8
new file mode 100644
index 0000000..639a9ac
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/isdntest.8
@@ -0,0 +1,115 @@
+.\"
+.\" Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdntest.8,v 1.10 1999/12/13 22:11:55 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Mon Dec 13 23:07:23 1999]
+.\"
+.Dd December 10, 1999
+.Dt ISDNTEST 8
+.Os
+.Sh NAME
+.Nm isdntest
+.Nd isdn4bsd debugging and verification tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar unit
+.Op Fl d Ar level
+.Op Fl i Ar number
+.Op Fl h
+.Op Fl o Ar number
+.Op Fl t Ar num
+.Op Fl w
+.Sh DESCRIPTION
+The
+.Nm
+utility is part of the isdn4bsd package and may be used as a stimulation tool
+for debugging the isdn4bsd kernel functionality.
+.Pp
+NOTE:
+The
+.Nm
+utility must be run
+.Em instead
+of the
+.Xr isdnd 8
+daemon and cannot be used while the daemon runs.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Use controller unit number for test.
+.It Fl d
+Set the debugging level
+.It Fl h
+Use HDLC as the B channel layer 1 protocol instead of no protocol.
+.It Fl i
+Use number to verify the incoming number.
+.It Fl o
+Use number as the outgoing number to dial.
+.It Fl t
+Set number of times the test pattern on the B-channel is exchanged
+.It Fl w
+Wait for keyboard input for terminating the call.
+.El
+.Pp
+The
+.Nm
+utility is currently of not much use for end users, it is primarily a debugging
+tool for development and is part of the release in the hope, that someone
+enhances it as a real self test and setup-verification tool!
+.Pp
+The
+.Nm
+utility does almost no error checking and error recovery, so unexpected
+hangs or crashes may occur.
+.Sh EXAMPLES
+For the following example, it is assumed that a machine with isdn4bsd
+installed is connected to an S0 bus and that one of the valid MSN's (MSN = Multiple Subscriber Number
+= telephone number) on this bus is
+.Em 42 .
+The
+.Xr isdnd 8
+.Em must
+not currently running on that machine! Executing:
+.Bd -literal -offset indent
+isdntest -i 42 -o 42
+.Ed
+.Pp
+will setup an outgoing call from that machine to itself, connect to itself
+and disconnect after 5 seconds.
+The
+.Nm
+utility has to be finished by the user by entering Control-C.
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /dev/i4b
+.El
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/isdntest/main.c b/usr.sbin/i4b/isdntest/main.c
new file mode 100644
index 0000000..2d80e2e
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/main.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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.c - i4b selftest utility
+ * -----------------------------
+ *
+ * $Id: main.c,v 1.16 2000/03/13 16:18:38 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Mar 13 17:19:26 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_cause.h>
+
+static void kbdrdhdl ( void );
+static void isdnrdhdl (int isdnfd );
+
+void handle_connect_ind(unsigned char *ptr);
+void handle_disconnect(unsigned char *ptr);
+void handle_connect_active_ind(unsigned char *ptr);
+
+int connect_response(int isdnfd, unsigned int cdid, int response);
+int disconnect_request(int isdnfd, unsigned int cdid);
+unsigned int get_cdid(int isdnfd);
+int connect_request(int isdnfd, unsigned int cdid);
+int do_test(void);
+static void cleanup(void);
+static void usage(void);
+void setup_wrfix(int len, unsigned char *buf);
+int check_rd(int len, unsigned char *wbuf, unsigned char *rdbuf);
+
+static int isdnfd;
+char outgoingnumber[32];
+char incomingnumber[32];
+int debug_level = 0;
+
+#define I4BDEVICE "/dev/i4b"
+#define DATADEV0 "/dev/i4brbch0"
+#define DATAUNIT0 0
+#define DATADEV1 "/dev/i4brbch1"
+#define DATAUNIT1 1
+
+unsigned int out_cdid = CDID_UNUSED;
+unsigned int in_cdid = CDID_UNUSED;
+
+int waitchar = 0;
+int usehdlc = 0;
+int controller = 0;
+int dotest = 0;
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdntest - i4b selftest, version %d.%d.%d, compiled %s %s\n",VERSION, REL, STEP, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdntest [-c ctrl] [-d level] [-h] [-i telno] [-o telno] [-t num] [-w]\n");
+ fprintf(stderr, " -c <ctrl> specify controller to use\n");
+ fprintf(stderr, " -d <level> set debug level\n");
+ fprintf(stderr, " -h use HDLC as Bchannel protocol\n");
+ fprintf(stderr, " -i <telno> incoming telephone number\n");
+ fprintf(stderr, " -o <telno> outgoing telephone number\n");
+ fprintf(stderr, " -t <num> send test pattern num times\n");
+ fprintf(stderr, " -w wait for keyboard entry to disconnect\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int i;
+ int c;
+ fd_set set;
+ int ret;
+ char *ptr;
+
+ incomingnumber[0] = '\0';
+ outgoingnumber[0] = '\0';
+
+ while ((c = getopt(argc, argv, "c:d:hi:o:t:w")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ if(isdigit(*optarg))
+ {
+ controller = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -c requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case 'd':
+ if(isdigit(*optarg))
+ {
+ debug_level = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -d requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case 'o':
+ i = 0;
+ ptr = optarg;
+
+ while(*ptr)
+ {
+ if(isdigit(*ptr))
+ {
+ outgoingnumber[i++] = *ptr++;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -o requires a numeric argument!\n");
+ usage();
+ }
+ }
+ outgoingnumber[i] = '\0';
+ break;
+
+ case 'i':
+ i = 0;
+ ptr = optarg;
+
+ while(*ptr)
+ {
+ if(isdigit(*ptr))
+ {
+ incomingnumber[i++] = *ptr++;
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -i requires a numeric argument!\n");
+ usage();
+ }
+ }
+ incomingnumber[i] = '\0';
+ break;
+
+ case 'w':
+ waitchar = 1;
+ break;
+
+ case 'h':
+ usehdlc = 1;
+ break;
+
+ case 't':
+ if(isdigit(*optarg))
+ {
+ dotest = strtoul(optarg, NULL, 10);
+ }
+ else
+ {
+ fprintf(stderr, "Error: option -t requires a numeric argument!\n");
+ usage();
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if((strlen(incomingnumber) == 0) || (strlen(outgoingnumber) == 0))
+ usage();
+
+ fprintf(stderr, "isdntest: accepting calls from telephone number [%s] \n", incomingnumber);
+ fprintf(stderr, "isdntest: calling out telephone number [%s] \n", outgoingnumber);
+
+ if((atexit(cleanup)) != 0)
+ {
+ fprintf(stderr, "isdntest: atexit error: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ /* open isdn device */
+
+ if((isdnfd = open(I4BDEVICE, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "\nisdntest: cannot open %s: %s\n", I4BDEVICE, strerror(errno));
+ fprintf(stderr, " isdnd is probably running, to use isdntest,\n");
+ fprintf(stderr, " terminate isdnd and then run isdntest again!\n");
+ exit(1);
+ }
+
+ if((out_cdid = get_cdid(isdnfd)) == 0)
+ {
+ fprintf(stderr, "isdntest: error getting cdid: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if((connect_request(isdnfd, out_cdid)) == -1)
+ {
+ fprintf(stderr, "isdntest: error, outgoing call failed!\n");
+ exit(1);
+ }
+
+ for(;;)
+ {
+ FD_ZERO(&set);
+
+ FD_SET(0, &set);
+
+ FD_SET(isdnfd, &set);
+
+ ret = select(isdnfd + 1, &set, NULL, NULL, NULL);
+
+ if(ret > 0)
+ {
+ if(FD_ISSET(isdnfd, &set))
+ isdnrdhdl(isdnfd);
+
+ if(FD_ISSET(0, &set))
+ kbdrdhdl();
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: select error: %s\n", strerror(errno));
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * data from keyboard available
+ *---------------------------------------------------------------------------*/
+static void
+kbdrdhdl(void)
+{
+ cleanup();
+ exit(2);
+}
+
+/*---------------------------------------------------------------------------*
+ * data from /dev/isdn available, read and process them
+ *---------------------------------------------------------------------------*/
+static void
+isdnrdhdl(int isdnfd)
+{
+ static unsigned char buf[1024];
+ int len;
+
+ if((len = read(isdnfd, buf, 1024 - 1)) > 0)
+ {
+ switch (buf[0])
+ {
+ case MSG_CONNECT_IND:
+ handle_connect_ind(&buf[0]);
+ break;
+
+ case MSG_CONNECT_ACTIVE_IND:
+ handle_connect_active_ind(&buf[0]);
+ break;
+
+ case MSG_DISCONNECT_IND:
+ handle_disconnect(&buf[0]);
+ break;
+
+ default:
+ if(debug_level)
+ fprintf(stderr, "isdntest: unknown message 0x%x = %c\n", buf[0], buf[0]);
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: read error, errno = %d, length = %d", errno, len);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * initiate an outgoing connection
+ *---------------------------------------------------------------------------*/
+int
+connect_request(int isdnfd, unsigned int cdid)
+{
+ msg_connect_req_t mcr;
+ int ret;
+
+ bzero(&mcr, sizeof(msg_connect_req_t));
+
+ mcr.controller = controller;
+ mcr.channel = CHAN_ANY; /* any channel */
+ mcr.cdid = cdid; /* cdid from get_cdid() */
+
+ if(usehdlc)
+ mcr.bprot = BPROT_RHDLC;/* b channel protocol */
+ else
+ mcr.bprot = BPROT_NONE; /* b channel protocol */
+
+ mcr.driver = BDRV_RBCH; /* raw b channel driver */
+ mcr.driver_unit = DATAUNIT0; /* raw b channel driver unit */
+
+ strcpy(mcr.dst_telno, outgoingnumber);
+ strcpy(mcr.src_telno, incomingnumber);
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_REQ, &mcr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CONNECT_REQ failed: %s", strerror(errno));
+ return(-1);
+ }
+ fprintf(stderr, "isdntest: calling out to telephone number [%s] \n", outgoingnumber);
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle setup indicator
+ *---------------------------------------------------------------------------*/
+void
+handle_connect_ind(unsigned char *ptr)
+{
+ msg_connect_ind_t *msi = (msg_connect_ind_t *)ptr;
+
+ fprintf(stderr, "isdntest: incoming SETUP: from %s to %s\n",
+ msi->src_telno,
+ msi->dst_telno);
+
+ fprintf(stderr, " channel %d, controller %d, bprot %d, cdid %d\n",
+ msi->channel,
+ msi->controller,
+ msi->bprot,
+ msi->header.cdid);
+
+ in_cdid = msi->header.cdid;
+
+ if(strcmp(msi->dst_telno, outgoingnumber))
+ {
+ msg_connect_resp_t msr;
+ int ret;
+
+ fprintf(stderr, "isdntest: ignoring incoming SETUP: my number [%s] != outgoing [%s]\n",
+ msi->dst_telno, outgoingnumber);
+
+ msr.cdid = in_cdid;
+ msr.response = SETUP_RESP_DNTCRE;
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &msr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CONNECT_RESP ignore failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ }
+ else
+ {
+ msg_connect_resp_t msr;
+ int ret;
+
+ fprintf(stderr, "isdntest: accepting call, sending CONNECT_RESPONSE .....\n");
+
+ msr.cdid = in_cdid;
+ msr.response = SETUP_RESP_ACCEPT;
+
+ if(usehdlc)
+ msr.bprot = BPROT_RHDLC;
+ else
+ msr.bprot = BPROT_NONE;
+
+ msr.driver = BDRV_RBCH;
+ msr.driver_unit = DATAUNIT1;
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &msr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CONNECT_RESP accept failed: %s", strerror(errno));
+ exit(1);
+ }
+ }
+}
+
+#define SLEEPTIME 5
+
+/*---------------------------------------------------------------------------*
+ * handle connection active
+ *---------------------------------------------------------------------------*/
+void
+handle_connect_active_ind(unsigned char *ptr)
+{
+ msg_connect_active_ind_t *msi = (msg_connect_active_ind_t *)ptr;
+ int i;
+
+ fprintf(stderr, "isdntest: connection active, cdid %d\n", msi->header.cdid);
+
+ if(out_cdid == msi->header.cdid)
+ {
+ if(waitchar)
+ {
+ fprintf(stderr, "isdntest: press any key to disconnect ...%c%c%c\n", 0x07, 0x07, 0x07);
+ getchar();
+ }
+ else
+ {
+ if(dotest)
+ {
+ do_test();
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: %d secs delay until disconnect:", SLEEPTIME);
+
+ for(i=0; i < SLEEPTIME;i++)
+ {
+ fprintf(stderr, " .");
+ sleep(1);
+ }
+ fprintf(stderr, "\n");
+ }
+ cleanup();
+ exit(0);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * handle disconnect indication
+ *---------------------------------------------------------------------------*/
+void
+handle_disconnect(unsigned char *ptr)
+{
+ msg_disconnect_ind_t *mdi = (msg_disconnect_ind_t *)ptr;
+
+ if(mdi->header.cdid == out_cdid)
+ {
+ fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (out_cdid), cause %d\n",
+ mdi->header.cdid, mdi->cause);
+
+ out_cdid = CDID_UNUSED;
+ }
+ else if(mdi->header.cdid == in_cdid)
+ {
+ fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (in_cdid), cause %d\n",
+ mdi->header.cdid, mdi->cause);
+ in_cdid = CDID_UNUSED;
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: incoming disconnect indication, cdid %d (???), cause %d\n",
+ mdi->header.cdid, mdi->cause);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * hang up
+ *---------------------------------------------------------------------------*/
+int
+disconnect_request(int isdnfd, unsigned int cdid)
+{
+ msg_discon_req_t mdr;
+ int ret;
+
+ mdr.cdid = cdid;
+ mdr.cause = (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL;
+
+ if((ret = ioctl(isdnfd, I4B_DISCONNECT_REQ, &mdr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_DISCONNECT_REQ failed: %s", strerror(errno));
+ return(-1);
+ }
+ fprintf(stderr, "isdntest: sending disconnect request\n");
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * get cdid from kernel
+ *---------------------------------------------------------------------------*/
+unsigned int
+get_cdid(int isdnfd)
+{
+ msg_cdid_req_t mcr;
+ int ret;
+
+ mcr.cdid = 0;
+
+ if((ret = ioctl(isdnfd, I4B_CDID_REQ, &mcr)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CDID_REQ failed: %s", strerror(errno));
+ return(0);
+ }
+ fprintf(stderr, "isdntest: got cdid %d from kernel\n", mcr.cdid);
+ return(mcr.cdid);
+}
+
+/*---------------------------------------------------------------------------*
+ * make shure all cdid's are inactive before leaving
+ *---------------------------------------------------------------------------*/
+void cleanup(void)
+{
+ int len;
+ char buf[1024];
+
+ if(out_cdid != CDID_UNUSED)
+ {
+ fprintf(stderr, "isdntest: cleanup, send disconnect req for out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
+ disconnect_request(isdnfd, out_cdid);
+ }
+
+ while((out_cdid != CDID_UNUSED) || (in_cdid != CDID_UNUSED))
+ {
+ if(debug_level)
+ fprintf(stderr, "isdntest: cleanup, out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
+
+ if((len = read(isdnfd, buf, 1024 - 1)) > 0)
+ {
+ switch (buf[0])
+ {
+ case MSG_CONNECT_IND:
+ handle_connect_ind(&buf[0]);
+ break;
+
+ case MSG_CONNECT_ACTIVE_IND:
+ handle_connect_active_ind(&buf[0]);
+ break;
+
+ case MSG_DISCONNECT_IND:
+ handle_disconnect(&buf[0]);
+ break;
+
+ default:
+ if(debug_level)
+ fprintf(stderr, "isdntest: unknown message 0x%x = %c\n", buf[0], buf[0]);
+ break;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "isdntest: read error, errno = %d, length = %d", errno, len);
+ }
+ }
+ if(debug_level)
+ fprintf(stderr, "isdntest: exit cleanup, out_cdid %d, in_cdid %d\n", out_cdid, in_cdid);
+}
+
+/*---------------------------------------------------------------------------*
+ * test the b-channels
+ *---------------------------------------------------------------------------*/
+int do_test(void)
+{
+
+#define FPH 0x3c
+#define FPL 0x66
+
+ int fd0, fd1;
+ unsigned char wrbuf[2048];
+ unsigned char rdbuf[2048];
+ int sz;
+ fd_set set;
+ struct timeval timeout;
+ int ret;
+ int frame;
+ int errcnt;
+ int frm_len;
+ int bytecnt = 0;
+ time_t start_time;
+ time_t cur_time;
+ time_t run_time;
+
+ if((fd0 = open(DATADEV0, O_RDWR)) == -1)
+ {
+ fprintf(stderr, "open of %s failed: %s", DATADEV0, strerror(errno));
+ return(-1);
+ }
+
+ if((fd1 = open(DATADEV1, O_RDWR)) == -1)
+ {
+ fprintf(stderr, "open of %s failed: %s", DATADEV1, strerror(errno));
+ return(-1);
+ }
+
+ printf("\n");
+ frame = 0;
+ errcnt = 0;
+
+ frm_len = 2;
+
+ start_time = time(NULL);
+
+ printf(" frame size errors totalbytes bps elapsedtime\n");
+
+ for(;dotest > 0; dotest--)
+ {
+ setup_wrfix(frm_len, &wrbuf[0]);
+
+ frame++;
+
+ bytecnt += frm_len;
+
+ printf("%6d %4d", frame, frm_len);
+ fflush(stdout);
+
+ if((sz = write(fd0, wrbuf, frm_len)) != frm_len)
+ {
+ fprintf(stderr, "write (%d of %d bytes) to %s failed: %s\n", sz, frm_len, DATADEV0, strerror(errno));
+ }
+
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&set);
+
+ FD_SET(0, &set);
+ FD_SET(fd1, &set);
+
+ ret = select(fd1+1, &set, NULL, NULL, &timeout);
+
+ if(ret > 0)
+ {
+ if(FD_ISSET(fd1, &set))
+ {
+ if((sz = read(fd1, rdbuf, 2048)) != frm_len)
+ {
+ fprintf(stderr, "read (%d bytes) from %s failed: %s\n", sz, DATADEV1, strerror(errno));
+ }
+
+ cur_time = time(NULL);
+ run_time = difftime(cur_time, start_time);
+
+ if(run_time == 0)
+ run_time = 1;
+
+ printf(" %6d %10d %4d %2.2d:%2.2d:%2.2d \r",
+ errcnt, bytecnt,
+ (int)((int)bytecnt/(int)run_time),
+ (int)run_time/3600, (int)run_time/60, (int)run_time%60);
+ fflush(stdout);
+
+ errcnt += check_rd(frm_len, &wrbuf[0], &rdbuf[0]);
+
+#ifdef NOTDEF
+ for(i=0; i<sz; i++)
+ {
+ printf("0x%02x ", (unsigned char)rdbuf[i]);
+ }
+ printf("\n");
+#endif
+ }
+
+ if(FD_ISSET(0, &set))
+ {
+ return(0);
+ printf("\n\n");
+ }
+ }
+ else
+ {
+ fprintf(stderr, "isdntest, do_test: select error: %s\n", strerror(errno));
+ }
+
+ frm_len = frm_len*2;
+ if(frm_len > 2048)
+ frm_len = 2;
+
+ }
+ printf("\n\n");
+ return(0);
+}
+
+void
+setup_wrfix(int len, unsigned char *buf)
+{
+ register int i;
+
+ for(i=0; i<len;)
+ {
+ *buf = FPH;
+ buf++;
+ *buf = FPL;
+ buf++;
+ i+=2;
+ }
+}
+
+int
+check_rd(int len, unsigned char *wbuf, unsigned char *rbuf)
+{
+ register int i;
+ int ret = 0;
+
+ for(i=0; i<len; i++)
+ {
+ if(*wbuf != *rbuf)
+ {
+ fprintf(stderr, "\nERROR, byte %d, written 0x%02x, read 0x%02x\n", i, *wbuf, *rbuf);
+ ret++;
+ }
+ wbuf++;
+ rbuf++;
+ }
+ return(ret);
+}
+
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/1tr6.c b/usr.sbin/i4b/isdntrace/1tr6.c
new file mode 100644
index 0000000..db2bc71
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/1tr6.c
@@ -0,0 +1,756 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * 1tr6.c - print 1TR6 protocol traces
+ * -----------------------------------
+ *
+ * $Id: 1tr6.c,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:55:31 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+static int p_1tr6address(char *pbuf, unsigned char buf[]);
+static int p_1tr6cause(char *pbuf, unsigned char buf[]);
+
+/*---------------------------------------------------------------------------*
+ * decode the (german) national specific 1TR6 protocol
+ *---------------------------------------------------------------------------*/
+void
+decode_1tr6(char *pbuf, int n, int off, unsigned char *buf, int raw)
+{
+ int codeset = 0;
+ int oldcodeset = 0;
+ int codelock = 0;
+
+ int pd;
+ int len;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ if(raw)
+ {
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i+off);
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "1TR6: ");
+
+ /* protocol discriminator */
+
+ i = 0;
+
+ pd = buf[i];
+
+ switch(pd)
+ {
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "pd=N0, ");
+ break;
+ case 0x41:
+ sprintf((pbuf+strlen(pbuf)), "pd=N1, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "pd=UNDEF (0x%02x), ",pd);
+ break;
+ }
+
+ /* call reference */
+
+ i++;
+
+ len = buf[i] & 0x0f;
+
+ switch(len)
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "cr=0x%02x %s, ", (buf[i+1] & 0x7f), (buf[i+1] & 0x80) ? "(from destination)" : "(from origination)");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "cr: LEN=%d %s 0x%02x 0x%02x, ", len, (buf[i+1] & 0x80) ? "org" : "dst", (buf[i+1] & 0x7f), (buf[i+2] & 0x7f));
+ break;
+ }
+
+ i += (len+1);
+
+ /* message type */
+
+ sprintf((pbuf+strlen(pbuf)), "message=");
+
+ if(pd == 0x40) /* protocol discriminator N0 */
+ {
+ switch(buf[i])
+ {
+ case 0x61:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER INDICATION: ");
+ break;
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL INDICATION: ");
+ break;
+ case 0x63:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY STATUS: ");
+ break;
+ case 0x64:
+ sprintf((pbuf+strlen(pbuf)), "STATUS ACKNOWLEDGE: ");
+ break;
+ case 0x65:
+ sprintf((pbuf+strlen(pbuf)), "STATUS REJECT: ");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY INFORMATION: ");
+ break;
+ case 0x67:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION ACKNOWLEDGE: ");
+ break;
+ case 0x68:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION REJECT: ");
+ break;
+ case 0x75:
+ sprintf((pbuf+strlen(pbuf)), "CLOSE: ");
+ break;
+ case 0x77:
+ sprintf((pbuf+strlen(pbuf)), "CLOSE ACKNOWLEDGE: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: PD=0x40 MSG=0x%02x, ", buf[i]);
+ break;
+ }
+ }
+ else if(pd == 0x41)
+ {
+ switch(buf[i])
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "ESCAPE: ");
+ break;
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "ALERT: ");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "CALL SENT: ");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT: ");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT ACKNOWLEDGE: ");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "SETUP: ");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "SETUP ACKNOWLEDGE: ");
+ break;
+
+ case 0x26:
+ sprintf((pbuf+strlen(pbuf)), "RESUME: ");
+ break;
+ case 0x2e:
+ sprintf((pbuf+strlen(pbuf)), "RESUME ACKNOWLEDGE: ");
+ break;
+ case 0x22:
+ sprintf((pbuf+strlen(pbuf)), "RESUME REJECT: ");
+ break;
+ case 0x25:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND: ");
+ break;
+ case 0x2d:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND ACKNOWLEDGE: ");
+ break;
+ case 0x21:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND REJECT: ");
+ break;
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "USER INFORMATION: ");
+ break;
+
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "DETACH");
+ break;
+ case 0x45:
+ sprintf((pbuf+strlen(pbuf)), "DISCONNECT: ");
+ break;
+ case 0x4d:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE: ");
+ break;
+ case 0x5a:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE ACKNOWLEDGE");
+ break;
+
+ case 0x6e:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL ACKNOWLEDGE: ");
+ break;
+ case 0x67:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL REJECT: ");
+ break;
+ case 0x69:
+ sprintf((pbuf+strlen(pbuf)), "CONGESTION CONTROL: ");
+ break;
+ case 0x60:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY: ");
+ break;
+ case 0x68:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY ACKNOWLEDGE: ");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY CANCEL: ");
+ break;
+ case 0x64:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY REGISTER: ");
+ break;
+ case 0x65:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY REJECT: ");
+ break;
+ case 0x6d:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION: ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER ACKNOWLEDGE: ");
+ break;
+ case 0x6f:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER REJECT: ");
+ break;
+ case 0x63:
+ sprintf((pbuf+strlen(pbuf)), "STATUS: ");
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: PD=0x41 MSG=0x%02x, ", buf[i]);
+ break;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "ERROR: PD=0x%02x MSG=0x%02x, ", pd, buf[i]);
+ }
+
+ /* other information elements */
+
+ i++;
+
+ for (; i < n;)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n ");
+
+ if(buf[i] & 0x80)
+ {
+ /* single octett info element */
+
+ switch(buf[i] & 0x70)
+ {
+ case 0x00: /* reserved */
+ sprintf((pbuf+strlen(pbuf)), "[reserved single octett info]");
+ break;
+
+ case 0x10: /* shift */
+ oldcodeset = codeset;
+ codeset = buf[i] & 0x07;
+ if(buf[i] & 0x08)
+ codelock = 0;
+ else
+ codelock = 1;
+ sprintf((pbuf+strlen(pbuf)), "[shift: codeset=%d lock=%d]", codeset, codelock);
+ break;
+
+ case 0x20: /* more data */
+ sprintf((pbuf+strlen(pbuf)), "[more data]");
+ break;
+
+ case 0x30: /* congestion level */
+ sprintf((pbuf+strlen(pbuf)), "[congestion level = %d]", buf[i] & 0x0f);
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNDEF SINGLE OCTET ELEMENT 0x%02x]", buf[i]);
+ break;
+ }
+
+ i++; /* next */
+
+ }
+ else
+ {
+ /* variable length info element */
+
+ if(codeset == 0)
+ {
+ switch(buf[i])
+ {
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "[cause: ");
+ i += p_1tr6cause(pbuf, &buf[i]);
+ goto next;
+ break;
+
+ case 0x0c:
+ sprintf((pbuf+strlen(pbuf)), "[connected address: ");
+ i += p_1tr6address(pbuf, &buf[i]);
+ goto next;
+ break;
+
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "[call identity: ");
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "[channel id: channel=");
+ i += 2;
+ switch(buf[i] & 0x03)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "no channel");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "B-1");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "B-2");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "any channel");
+ break;
+ }
+ if(buf[i] & 0x08)
+ sprintf((pbuf+strlen(pbuf)), " (exclusive)]");
+ else
+ sprintf((pbuf+strlen(pbuf)), " (preferred)]");
+ i++;
+ goto next;
+ break;
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "[network specific facilities: ");
+ i++;
+ len = buf[i];
+ i+=2;
+ switch(buf[i])
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Sperre");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "AWS 1");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "AWS 2");
+ break;
+ case 0xe:
+ sprintf((pbuf+strlen(pbuf)), "Konferenz");
+ break;
+ case 0xf:
+ sprintf((pbuf+strlen(pbuf)), "B-Kan uebern.");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "aktvrg. ghlt. Vbdg.");
+ break;
+ case 0x11:
+ sprintf((pbuf+strlen(pbuf)), "3er Konf");
+ break;
+ case 0x12:
+ sprintf((pbuf+strlen(pbuf)), "1seitg D/G Wechsel");
+ break;
+ case 0x13:
+ sprintf((pbuf+strlen(pbuf)), "2seitig D/G Wechsel");
+ break;
+ case 0x14:
+ sprintf((pbuf+strlen(pbuf)), "Rufnr. identifiz.");
+ break;
+ case 0x15:
+ sprintf((pbuf+strlen(pbuf)), "GBG");
+ break;
+ case 0x17:
+ sprintf((pbuf+strlen(pbuf)), "ueberg. Ruf");
+ break;
+ case 0x1a:
+ sprintf((pbuf+strlen(pbuf)), "um/weitergel. Ruf");
+ break;
+ case 0x1b:
+ sprintf((pbuf+strlen(pbuf)), "unterdr. A-Rufnr.");
+ break;
+ case 0x1e:
+ sprintf((pbuf+strlen(pbuf)), "Verbdg. deaktivieren");
+ break;
+ case 0x1d:
+ sprintf((pbuf+strlen(pbuf)), "Verbdg. aktivieren");
+ break;
+ case 0x1f:
+ sprintf((pbuf+strlen(pbuf)), "SPV");
+ break;
+ case 0x23:
+ sprintf((pbuf+strlen(pbuf)), "Rueckw. 2seitg. DW");
+ break;
+ case 0x24:
+ sprintf((pbuf+strlen(pbuf)), "Anrufumltg. priv. Netz");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined");
+ break;
+ }
+ i++;
+ sprintf((pbuf+strlen(pbuf)), ", serv=%d", buf[i]);
+ i++;
+ sprintf((pbuf+strlen(pbuf)), ", ainfo=%d", buf[i]);
+ i++;
+ len-=4;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x28:
+ sprintf((pbuf+strlen(pbuf)), "[display: ");
+ break;
+ case 0x2c:
+ sprintf((pbuf+strlen(pbuf)), "[keypad: ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "[origination address: ");
+ i += p_1tr6address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x70:
+ sprintf((pbuf+strlen(pbuf)), "[destination address: ");
+ i += p_1tr6address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x7e:
+ sprintf((pbuf+strlen(pbuf)), "[user-user information: ");
+ break;
+ case 0x7f:
+ sprintf((pbuf+strlen(pbuf)), "[reserved: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN INFO-ELEMENT-ID");
+ break;
+ }
+ }
+ else if(codeset == 6)
+ {
+ switch(buf[i])
+ {
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "[service ind: serv=");
+ i+= 2;
+ switch(buf[i])
+ {
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "phone");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "a/b");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "X.21");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "fax g4");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "btx");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "64k data");
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "X.25");
+ break;
+ case 0x09:
+ sprintf((pbuf+strlen(pbuf)), "teletex");
+ break;
+ case 0x0a:
+ sprintf((pbuf+strlen(pbuf)), "mixed");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "temex");
+ break;
+ case 0x0e:
+ sprintf((pbuf+strlen(pbuf)), "picturephone");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "btx (new)");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "videophone");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined");
+ break;
+ }
+ i++;
+ sprintf((pbuf+strlen(pbuf)), ", ainfo=0x%02x]", buf[i]);
+ i++;
+ goto next;
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "[charging information: ");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "[date: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "[facility select: ");
+ break;
+ case 0x06:
+ sprintf((pbuf+strlen(pbuf)), "[status of facilities: ");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "[status of called party: ");
+ i+=2;
+ switch(buf[i])
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "no information]");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "is being called]");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined (0x%02x)]", buf[i]);
+ break;
+ }
+ i++;
+ goto next;
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "[additional tx attributes: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ switch(buf[j+i] &0x70)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "no satellite link");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "one satellite link");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "two satellite links");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "three satellite links");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "undefined value");
+ break;
+ }
+ if(buf[j+i] & 0x80)
+ sprintf((pbuf+strlen(pbuf)),"(flag=req)]");
+ else
+ sprintf((pbuf+strlen(pbuf)),"(flag=ind)]");
+ }
+ i += j;
+ goto next;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN INFO-ELEMENT-ID");
+ break;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "[ILLEGAL CODESET = 0x%02x", codeset);
+ }
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++; /* index -> 1st param */
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += len;
+
+next:
+
+ if(!codelock && (codeset != oldcodeset))
+ codeset = oldcodeset;
+ }
+ }
+ sprintf((pbuf+strlen(pbuf)),"\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the cause
+ *---------------------------------------------------------------------------*/
+static int
+p_1tr6cause(char *pbuf, unsigned char buf[])
+{
+ int j;
+ int len;
+ int i = 0;
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ switch(len)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "%s", print_cause_1tr6(0));
+ break;
+ case 1:
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "%s", print_cause_1tr6(buf[i] & 0x7f));
+ break;
+ case 2:
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "%s, location: ", print_cause_1tr6(buf[i] & 0x7f));
+ i++;
+ switch(buf[i] & 0x0f)
+ {
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "public network");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "private network");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "no information");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ break;
+ default:
+ i++; /* index -> length */
+ len = buf[i];
+ i++; /* index -> 1st param */
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+ }
+ break;
+ }
+ i++;
+ sprintf((pbuf+strlen(pbuf)),"]");
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the ISDN (telephone) number
+ *---------------------------------------------------------------------------*/
+static int
+p_1tr6address(char *pbuf, unsigned char buf[])
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+
+ i++; /* index -> length */
+ len = buf[i];
+ i++; /* index -> 1st param */
+ tp = buf[i];
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " (type=unknown, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " (type=international, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " (type=national, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " (type=%d, ", ((tp & 0x70) >> 4));
+ break;
+ }
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "plan=unknown)");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "plan=ISDN)");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "plan=%d)", (tp & 0x0f));
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += j;
+
+ return(i);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/Makefile b/usr.sbin/i4b/isdntrace/Makefile
new file mode 100644
index 0000000..eae0919
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= isdntrace
+MAN= isdntrace.8
+SRCS= q921.c q931.c q931_util.c q932_fac.c 1tr6.c trace.c \
+ pcause_1tr6.c pcause_q850.c unknownl3.c
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/i4b/isdntrace/cable.txt b/usr.sbin/i4b/isdntrace/cable.txt
new file mode 100644
index 0000000..21a1cdb
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/cable.txt
@@ -0,0 +1,62 @@
+ *---------------------------------------------------------------------------
+ *
+ * Custom cable to trace an ISDN S0 bus with two passive (!) ISDN boards
+ * ---------------------------------------------------------------------
+ *
+ * $Id: cable.txt,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:55:42 1999]
+ *
+ * -hm documentation of analyze mode
+ *
+ *---------------------------------------------------------------------------*/
+
+The cable consists of a RJ-45 plug with both tx and rx connected and
+two jacks; the tx cables from the plug are wired to one jack and the
+rx cables from the plug are wired to the other jack.
+
+The computer must be equipped with two (!) supported passive cards and
+the cable from one card is plugged into one of the jacks while the cable
+to from the other card is plugged into the other jack.
+
+Now one card monitors the tx part of the S0 bus and the other card
+monitors the rx part.
+
+Which card functions as the rx side and which as the tx side can be
+specified as options to the isdntrace utility (-R and -T) which has
+to be run in analyzer mode (-a) to support this configuration.
+
+
+ 1
+ 2
+ 3
+ +--------------4 receiving-side board
+ S0-bus +--|--------------5 in computer (jack for
+ to analyze | | 6 cable to passive controller)
+ 8 | | 7
+ 7 | | 8
+ transmit - 6------------|--|--+
+ receive - 5------------+ | |
+ receive + 4---------------+ |
+ transmit + 3------------+ |
+ 2 | | 1
+ 1 | | 2
+ plug into | | 3
+ S0 bus +-----|-----------4 transmitting-side board
+ +-----------5 in computer (jack for
+ 6 cable to passive controller)
+ 7
+ 8
+
+
+
+ RJ-45 plug RJ-45 jack
+ view from the front view from the front
+ cable goes out to the rear
+
+ /--------- / ----------
+ | 87654321 | | 12345678 |
+ |__ __|/ |/_ /_|
+ |____|/ |/___|
diff --git a/usr.sbin/i4b/isdntrace/isdntrace.8 b/usr.sbin/i4b/isdntrace/isdntrace.8
new file mode 100644
index 0000000..5c3011b
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/isdntrace.8
@@ -0,0 +1,222 @@
+.\"
+.\" Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: isdntrace.8,v 1.14 2000/02/13 15:26:52 hm Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Wed Nov 1 15:52:28 2000]
+.\"
+.Dd November 1, 2000
+.Dt ISDNTRACE 8
+.Os
+.Sh NAME
+.Nm isdntrace
+.Nd isdn4bsd ISDN protocol trace utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl b
+.Op Fl d
+.Op Fl f Ar filename
+.Op Fl h
+.Op Fl i
+.Op Fl l
+.Op Fl n Ar number
+.Op Fl o
+.Op Fl p Ar filename
+.Op Fl r
+.Op Fl u Ar number
+.Op Fl x
+.Op Fl B
+.Op Fl F
+.Op Fl P
+.Op Fl R Ar unit
+.Op Fl T Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility is part of the isdn4bsd package and is used to provide the user with a
+mnemonic display of the layers 1, 2 and 3 protocol activities on
+the D channel and hex dump of the B channel(s) activities.
+.Pp
+Together with two passive supported cards and an easy to build cable it can
+also be used to monitor the complete traffic on a S0 bus providing S0 bus
+analyzer features.
+.Pp
+The
+.Nm
+utility is only available for passive supported cards.
+.Pp
+.Em Note
+.Pp
+All filenames, user specified or default, get a date and time stamp string
+added in the form -yyyymmdd-hhmmss: a hyphen, four digits year, two digits
+month and day, a hyphen and two digits hour, minutes and seconds.
+Tracefiles no longer get overwritten.
+In case a new filename is needed within a second, the filename-generating
+mechanism sleeps one second.
+.Pp
+In case the program is sent a USR1 signal, a new user specified or default
+filename with a new date and timestamp is generated and opened.
+.Pp
+The following options can be used:
+.Bl -tag -width Ds
+.It Fl a
+Run
+.Nm
+in analyzer mode by using two passive cards and a custom cable which can
+be build as described in the file
+.Em cable.txt
+in the isdn4bsd source distribution. One card acts as a receiver for the
+transmitting direction on the S0 bus while the other card acts as a receiver
+for the receiving direction on the S0 bus. Complete traffic monitoring is
+possible using this setup.
+.It Fl b
+switch B channel tracing on (default off).
+.It Fl d
+switch D channel tracing off (default on).
+.It Fl f
+Use
+.Ar filename
+as the name of a file into which to write tracing output (default filename is
+isdntrace<n> where n is the number of the unit to trace).
+.It Fl h
+switch display of header off (default on).
+.It Fl i
+print layer 1 (I.430) INFO signals to monitor layer 1 activity (default off).
+.It Fl l
+switch displaying of Layer 2 (Q.921) frames off (default on).
+.It Fl n
+This option takes a numeric argument specifying the minimum
+frame size in octets a frame must have to be displayed. (default 0)
+.It Fl o
+switch off writing trace output to a file (default on).
+.It Fl p
+Use
+.Ar filename
+as the name of a file used for the -B and -P options (default filename
+is isdntracebin<n> where n is the number of the unit to trace).
+.It Fl r
+Switch off printing a raw hexadecimal dump of the packets preceding
+the decoded protocol information (default on).
+.It Fl u
+Use
+.Ar number
+as the unit number of the controller card to trace (default 0).
+.It Fl x
+Switch on printing of packets with a non-Q.931 protocol discriminator.
+(default off).
+.It Fl B
+Write undecoded binary trace data to a file for later or remote
+analyzing (default off).
+.It Fl F
+This option can only be used when option -P (playback from binary data file)
+is used. The -F option causes playback not to stop at end of file but rather
+to wait for additional data to be available from the input file.
+.Pp
+This option is useful when trace data is accumulated in binary format (to
+save disk space) but a monitoring functionality is desired.
+(default off).
+.It Fl P
+Read undecoded binary trace data from file instead from device (default off).
+.It Fl R
+Use
+.Ar unit
+as the receiving interface unit number in analyze mode.
+.It Fl T
+Use
+.Ar unit
+as the transmitting interface unit number in analyze mode.
+.El
+.Pp
+When the USR1 signal is sent to a
+.Nm
+process, the currently used logfiles are reopened, so that logfile
+rotation becomes possible.
+.Pp
+The trace output should be obvious. It is very handy to have the following
+standard texts available when tracing ISDN protocols:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I.430
+ISDN BRI layer 1 protocol description.
+.It Ar Q.921
+ISDN D-channel layer 2 protocol description.
+.It Ar Q.931
+ISDN D-channel layer 3 protocol description.
+.It Ar 1TR6
+German-specific ISDN layer 3 protocol description. (NOTICE: decoding
+of the 1TR6 protocol is included but not supported since i dont have
+any longer access to a 1TR6 based ISDN installation.)
+.El
+.Pp
+The
+.Nm
+utility
+automatically detects the layer 3 protocol being used by looking at the
+Protocol Discriminator (see: Q.931/1993 pp. 53).
+.Sh FILES
+.Bl -tag -width daddeldi -compact
+.It Pa /dev/i4btrc<n>
+The devicefile(s) used to get the trace messages for ISDN card unit <n>
+out of the kernel.
+.El
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+isdntrace -f /var/tmp/isdn.trace
+.Ed
+.Pp
+will start D channel tracing on passive controller 0 with all except B
+channel tracing enabled and logs everything into the output file
+/var/tmp/isdn.trace-yyyymmdd-hhmmss (where yyyymmdd and hhmmss are replaced
+by the current date and time values).
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh BUGS
+Still some or more left.
+.Sh STANDARDS
+ITU Recommendations I.430, Q.920, Q.921, Q.930, Q.931
+.Pp
+FTZ Richtlinie 1TR3, Band III
+.Pp
+ITU Recommendation Q.932 (03/93), Q.950 (03/93)
+.Pp
+ETSI Recommendation ETS 300 179 (10/92), ETS 300 180 (10/92)
+.Pp
+ETSI Recommendation ETS 300 181 (04/93), ETS 300 182 (04/93)
+.Pp
+ITU Recommendation X.208, X.209
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Gary Jennejohn Aq gj@FreeBSD.org
+and
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
+.Pp
+This manual page was written by
+.An Hellmuth Michaelis .
diff --git a/usr.sbin/i4b/isdntrace/pcause_1tr6.c b/usr.sbin/i4b/isdntrace/pcause_1tr6.c
new file mode 100644
index 0000000..b319d74
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_1tr6.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * printing cause values
+ * ---------------------
+ *
+ * $Id: pcause_1tr6.c,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:03 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+#include "pcause_1tr6.h"
+
+char *
+print_cause_1tr6(unsigned char code)
+{
+ static char error_message[120];
+ char *e;
+
+ switch(code)
+ {
+ case CAUSE_1TR6_SHUTDN:
+ e = "normal D-channel shutdown";
+ break;
+
+ case CAUSE_1TR6_ICRV:
+ e = "invalid call reference value";
+ break;
+
+ case CAUSE_1TR6_BSNI:
+ e = "bearer service not implemented";
+ break;
+
+ case CAUSE_1TR6_CIDNE:
+ e = "call identity does not exist";
+ break;
+
+ case CAUSE_1TR6_CIIU:
+ e = "call identity in use";
+ break;
+
+ case CAUSE_1TR6_NCA:
+ e = "no channel available";
+ break;
+
+ case CAUSE_1TR6_RFNI:
+ e = "requested facility not implemented";
+ break;
+
+ case CAUSE_1TR6_RFNS:
+ e = "requested facility not subscribed";
+ break;
+
+ case CAUSE_1TR6_OCB:
+ e = "outgoing calls barred";
+ break;
+
+ case CAUSE_1TR6_UAB:
+ e = "user access busy";
+ break;
+
+ case CAUSE_1TR6_NECUG:
+ e = "non existent CUG";
+ break;
+
+ case CAUSE_1TR6_NECUG1:
+ e = "non existent CUG";
+ break;
+
+ case CAUSE_1TR6_SPV:
+ e = "kommunikationsbeziehung als SPV nicht erlaubt";
+ break;
+
+ case CAUSE_1TR6_DNO:
+ e = "destination not obtainable";
+ break;
+
+ case CAUSE_1TR6_NC:
+ e = "number changed";
+ break;
+
+ case CAUSE_1TR6_OOO:
+ e = "out of order";
+ break;
+
+ case CAUSE_1TR6_NUR:
+ e = "no user responding";
+ break;
+
+ case CAUSE_1TR6_UB:
+ e = "user busy";
+ break;
+
+ case CAUSE_1TR6_ICB:
+ e = "incoming calls barred";
+ break;
+
+ case CAUSE_1TR6_CR:
+ e = "call rejected";
+ break;
+
+ case CAUSE_1TR6_NCO:
+ e = "network congestion";
+ break;
+
+ case CAUSE_1TR6_RUI:
+ e = "remote user initiated";
+ break;
+
+ case CAUSE_1TR6_LPE:
+ e = "local procedure error";
+ break;
+
+ case CAUSE_1TR6_RPE:
+ e = "remote procedure error";
+ break;
+
+ case CAUSE_1TR6_RUS:
+ e = "remote user suspended";
+ break;
+
+ case CAUSE_1TR6_RUR:
+ e = "remote user resumed";
+ break;
+
+ case CAUSE_1TR6_UIDL:
+ e = "user info discharded locally";
+ break;
+
+ default:
+ e = "UNKNOWN error occured";
+ break;
+ }
+
+ sprintf(error_message, "0x%02x: %s", code & 0x7f, e);
+ return(error_message);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/pcause_1tr6.h b/usr.sbin/i4b/isdntrace/pcause_1tr6.h
new file mode 100644
index 0000000..67f50de
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_1tr6.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcause1tr6.h - 1TR6 causes definitions
+ * --------------------------------------
+ *
+ * $Id: pcause_1tr6.h,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:10 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+char *print_cause_1tr6(unsigned char code);
+
+/* 1TR6 protocol causes */
+
+#define CAUSE_1TR6_SHUTDN 0x00 /* normal D-channel shutdown */
+#define CAUSE_1TR6_ICRV 0x01 /* invalid call reference value */
+#define CAUSE_1TR6_BSNI 0x03 /* bearer service not implemented */
+#define CAUSE_1TR6_CIDNE 0x07 /* call identity does not exist */
+#define CAUSE_1TR6_CIIU 0x08 /* call identity in use */
+#define CAUSE_1TR6_NCA 0x0A /* no channel available */
+#define CAUSE_1TR6_RFNI 0x10 /* requested facility not implemented */
+#define CAUSE_1TR6_RFNS 0x11 /* requested facility not subscribed */
+#define CAUSE_1TR6_OCB 0x20 /* outgoing calls barred */
+#define CAUSE_1TR6_UAB 0x21 /* user access busy */
+#define CAUSE_1TR6_NECUG 0x22 /* non existent CUG */
+#define CAUSE_1TR6_NECUG1 0x23 /* non existent CUG */
+#define CAUSE_1TR6_SPV 0x25 /* kommunikationsbeziehung als SPV nicht erlaubt */
+#define CAUSE_1TR6_DNO 0x35 /* destination not obtainable */
+#define CAUSE_1TR6_NC 0x38 /* number changed */
+#define CAUSE_1TR6_OOO 0x39 /* out of order */
+#define CAUSE_1TR6_NUR 0x3A /* no user responding */
+#define CAUSE_1TR6_UB 0x3B /* user busy */
+#define CAUSE_1TR6_ICB 0x3D /* incoming calls barred */
+#define CAUSE_1TR6_CR 0x3E /* call rejected */
+#define CAUSE_1TR6_NCO 0x59 /* network congestion */
+#define CAUSE_1TR6_RUI 0x5A /* remote user initiated */
+#define CAUSE_1TR6_LPE 0x70 /* local procedure error */
+#define CAUSE_1TR6_RPE 0x71 /* remote procedure error */
+#define CAUSE_1TR6_RUS 0x72 /* remote user suspended */
+#define CAUSE_1TR6_RUR 0x73 /* remote user resumed */
+#define CAUSE_1TR6_UIDL 0x7F /* user info discharded locally */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/pcause_q850.c b/usr.sbin/i4b/isdntrace/pcause_q850.c
new file mode 100644
index 0000000..57eaf32
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_q850.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * printing cause values
+ * ---------------------
+ *
+ * $Id: pcause_q850.c,v 1.6 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:18 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+#include "pcause_q850.h"
+
+char *
+print_cause_q850(unsigned char code)
+{
+ static char error_message[120];
+ char *e;
+
+ switch(code)
+ {
+ case CAUSE_Q850_SHUTDN:
+ e = "normal D-channel shutdown";
+ break;
+
+ case CAUSE_Q850_NUNALLC:
+ e = "Unallocated (unassigned) number";
+ break;
+
+ case CAUSE_Q850_NRTTN:
+ e = "No route to specified transit network";
+ break;
+
+ case CAUSE_Q850_NRTDST:
+ e = "No route to destination";
+ break;
+
+ case CAUSE_Q850_SSINFTN:
+ e = "Send special information tone";
+ break;
+
+ case CAUSE_Q850_MDIALTP:
+ e = "Misdialled trunk prefix";
+ break;
+
+ case CAUSE_Q850_CHUNACC:
+ e = "Channel unacceptable";
+ break;
+
+ case CAUSE_Q850_CALLAWD:
+ e = "Call awarded and being delivered in an established channel";
+ break;
+
+ case CAUSE_Q850_PREEMPT:
+ e = "Preemption";
+ break;
+
+ case CAUSE_Q850_PREECRR:
+ e = "Preemption - circuit reserved for reuse";
+ break;
+
+ case CAUSE_Q850_NCCLR:
+ e = "Normal call clearing";
+ break;
+
+ case CAUSE_Q850_USRBSY:
+ e = "User busy";
+ break;
+
+ case CAUSE_Q850_NOUSRRSP:
+ e = "No user responding";
+ break;
+
+ case CAUSE_Q850_NOANSWR:
+ e = "No answer from user (user alerted)";
+ break;
+
+ case CAUSE_Q850_SUBSABS:
+ e = "Subscriber absent";
+ break;
+
+ case CAUSE_Q850_CALLREJ:
+ e = "Call rejected";
+ break;
+
+ case CAUSE_Q850_NUCHNG:
+ e = "Number changed";
+ break;
+
+ case CAUSE_Q850_NONSELUC:
+ e = "Non-selected user clearing";
+ break;
+
+ case CAUSE_Q850_DSTOOORDR:
+ e = "Destination out of order";
+ break;
+
+ case CAUSE_Q850_INVNUFMT:
+ e = "Invalid number format";
+ break;
+
+ case CAUSE_Q850_FACREJ:
+ e = "Facility rejected";
+ break;
+
+ case CAUSE_Q850_STENQRSP:
+ e = "Response to STATUS ENQUIRY";
+ break;
+
+ case CAUSE_Q850_NORMUNSP:
+ e = "Normal, unspecified";
+ break;
+
+ case CAUSE_Q850_NOCAVAIL:
+ e = "No circuit / channel available";
+ break;
+
+ case CAUSE_Q850_NETOOORDR:
+ e = "Network out of order";
+ break;
+
+ case CAUSE_Q850_PFMCDOOSERV:
+ e = "Permanent frame mode connection out of service";
+ break;
+
+ case CAUSE_Q850_PFMCOPER:
+ e = "Permanent frame mode connection operational";
+ break;
+
+ case CAUSE_Q850_TMPFAIL:
+ e = "Temporary failure";
+ break;
+
+ case CAUSE_Q850_SWEQCONG:
+ e = "Switching equipment congestion";
+ break;
+
+ case CAUSE_Q850_ACCINFDIS:
+ e = "Access information discarded";
+ break;
+
+ case CAUSE_Q850_REQCNOTAV:
+ e = "Requested circuit/channel not available";
+ break;
+
+ case CAUSE_Q850_PRECALBLK:
+ e = "Precedence call blocked";
+ break;
+
+ case CAUSE_Q850_RESUNAVAIL:
+ e = "Resources unavailable, unspecified";
+ break;
+
+ case CAUSE_Q850_QOSUNAVAIL:
+ e = "Quality of service unavailable";
+ break;
+
+ case CAUSE_Q850_REQSERVNS:
+ e = "Requested facility not subscribed";
+ break;
+
+ case CAUSE_Q850_OCBARRCUG:
+ e = "Outgoing calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_ICBARRCUG:
+ e = "Incoming calls barred within CUG";
+ break;
+
+ case CAUSE_Q850_BCAPNAUTH:
+ e = "Bearer capability not authorized";
+ break;
+
+ case CAUSE_Q850_BCAPNAVAIL:
+ e = "Bearer capability not presently available";
+ break;
+
+ case CAUSE_Q850_INCSTOACISC:
+ e = "Inconsistenciy in designated outg. access info and subscriber class";
+ break;
+
+ case CAUSE_Q850_SOONOTAVAIL:
+ e = "Service or option not available, unspecified";
+ break;
+
+ case CAUSE_Q850_BCAPNOTIMPL:
+ e = "Bearer capability not implemented";
+ break;
+
+ case CAUSE_Q850_CHTYPNIMPL:
+ e = "Channel type not implemented";
+ break;
+
+ case CAUSE_Q850_REQFACNIMPL:
+ e = "Requested facility not implemented";
+ break;
+
+ case CAUSE_Q850_ORDINBCAVL:
+ e = "Only restricted digital information bearer capability is available";
+ break;
+
+ case CAUSE_Q850_SOONOTIMPL:
+ e = "Service or option not implemented, unspecified";
+ break;
+
+ case CAUSE_Q850_INVCLRFVAL:
+ e = "Invalid call reference value";
+ break;
+
+ case CAUSE_Q850_IDCHDNOEX:
+ e = "Identified channel does not exist";
+ break;
+
+ case CAUSE_Q850_SUSCAEXIN:
+ e = "A suspended call exists, but this call identity does not";
+ break;
+
+ case CAUSE_Q850_CLIDINUSE:
+ e = "Call identity in use";
+ break;
+
+ case CAUSE_Q850_NOCLSUSP:
+ e = "No call suspended";
+ break;
+
+ case CAUSE_Q850_CLIDCLRD:
+ e = "Call having the requested call identity has been cleared";
+ break;
+
+ case CAUSE_Q850_UNOTMEMCUG:
+ e = "User not member of CUG";
+ break;
+
+ case CAUSE_Q850_INCDEST:
+ e = "Incompatible destination";
+ break;
+
+ case CAUSE_Q850_NONEXCUG:
+ e = "Non-existent CUG";
+ break;
+
+ case CAUSE_Q850_INVNTWSEL:
+ e = "Invalid transit network selection";
+ break;
+
+ case CAUSE_Q850_INVMSG:
+ e = "Invalid message, unspecified";
+ break;
+
+ case CAUSE_Q850_MIEMISS:
+ e = "Mandatory information element is missing";
+ break;
+
+ case CAUSE_Q850_MSGTNI:
+ e = "Message type non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_MSGNCMPT:
+ e = "Msg incompatible with call state/message type non-existent/not implemented";
+ break;
+
+ case CAUSE_Q850_IENENI:
+ e = "Information element/parameter non-existent or not implemented";
+ break;
+
+ case CAUSE_Q850_INVIEC:
+ e = "Invalid information element contents";
+ break;
+
+ case CAUSE_Q850_MSGNCWCS:
+ e = "Message not compatible with call state";
+ break;
+
+ case CAUSE_Q850_RECOTIMEXP:
+ e = "Recovery on timer expiry";
+ break;
+
+ case CAUSE_Q850_PARMNENIPO:
+ e = "Parameter non-existent or not implemented, passed on";
+ break;
+
+ case CAUSE_Q850_MSGUNRDPRM:
+ e = "Message with unrecognized parameter, discarded";
+ break;
+
+ case CAUSE_Q850_PROTERR:
+ e = "Protocol error, unspecified";
+ break;
+
+ case CAUSE_Q850_INTWRKU:
+ e = "Interworking, unspecified";
+ break;
+
+ default:
+ e = "ERROR, unknown cause value!";
+ break;
+ }
+
+ sprintf(error_message, "%d: %s (Q.850)", code, e);
+ return(error_message);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/pcause_q850.h b/usr.sbin/i4b/isdntrace/pcause_q850.h
new file mode 100644
index 0000000..ea21770
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_q850.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * pcauseq850.h - Q.850 causes definitions
+ * ---------------------------------------
+ *
+ * $Id: pcause_q850.h,v 1.5 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:25 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+char *print_cause_q850(unsigned char code);
+
+/* Q.850 causes */
+
+#define CAUSE_Q850_SHUTDN 0x00 /* normal D-channel shutdown */
+#define CAUSE_Q850_NUNALLC 0x01 /* Unallocated (unassigned) number */
+#define CAUSE_Q850_NRTTN 0x02 /* No route to specified transit network */
+#define CAUSE_Q850_NRTDST 0x03 /* No route to destination */
+#define CAUSE_Q850_SSINFTN 0x04 /* Send special information tone */
+#define CAUSE_Q850_MDIALTP 0x05 /* Misdialled trunk prefix */
+#define CAUSE_Q850_CHUNACC 0x06 /* Channel unacceptable */
+#define CAUSE_Q850_CALLAWD 0x07 /* Call awarded and being delivered in an established channel */
+#define CAUSE_Q850_PREEMPT 0x08 /* Preemption */
+#define CAUSE_Q850_PREECRR 0x09 /* Preemption - circuit reserved for reuse */
+#define CAUSE_Q850_NCCLR 0x10 /* Normal call clearing */
+#define CAUSE_Q850_USRBSY 0x11 /* User busy */
+#define CAUSE_Q850_NOUSRRSP 0x12 /* No user responding */
+#define CAUSE_Q850_NOANSWR 0x13 /* No answer from user (user alerted) */
+#define CAUSE_Q850_SUBSABS 0x14 /* Subscriber absent */
+#define CAUSE_Q850_CALLREJ 0x15 /* Call rejected */
+#define CAUSE_Q850_NUCHNG 0x16 /* Number changed */
+#define CAUSE_Q850_NONSELUC 0x1A /* Non-selected user clearing */
+#define CAUSE_Q850_DSTOOORDR 0x1B /* Destination out of order */
+#define CAUSE_Q850_INVNUFMT 0x1C /* Invalid number format */
+#define CAUSE_Q850_FACREJ 0x1D /* Facility rejected */
+#define CAUSE_Q850_STENQRSP 0x1E /* Response to STATUS ENQUIRY */
+#define CAUSE_Q850_NORMUNSP 0x1F /* Normal, unspecified */
+#define CAUSE_Q850_NOCAVAIL 0x22 /* No circuit / channel available */
+#define CAUSE_Q850_NETOOORDR 0x26 /* Network out of order */
+#define CAUSE_Q850_PFMCDOOSERV 0x27 /* Permanent frame mode connection out of service */
+#define CAUSE_Q850_PFMCOPER 0x28 /* Permanent frame mode connection operational */
+#define CAUSE_Q850_TMPFAIL 0x29 /* Temporary failure */
+#define CAUSE_Q850_SWEQCONG 0x2A /* Switching equipment congestion */
+#define CAUSE_Q850_ACCINFDIS 0x2B /* Access information discarded */
+#define CAUSE_Q850_REQCNOTAV 0x2C /* Requested circuit/channel not available */
+#define CAUSE_Q850_PRECALBLK 0x2E /* Precedence call blocked */
+#define CAUSE_Q850_RESUNAVAIL 0x2F /* Resources unavailable, unspecified */
+#define CAUSE_Q850_QOSUNAVAIL 0x31 /* Quality of service unavailable */
+#define CAUSE_Q850_REQSERVNS 0x32 /* Requested facility not subscribed */
+#define CAUSE_Q850_OCBARRCUG 0x35 /* Outgoing calls barred within CUG */
+#define CAUSE_Q850_ICBARRCUG 0x36 /* Incoming calls barred within CUG */
+#define CAUSE_Q850_BCAPNAUTH 0x39 /* Bearer capability not authorized */
+#define CAUSE_Q850_BCAPNAVAIL 0x3A /* Bearer capability not presently available */
+#define CAUSE_Q850_INCSTOACISC 0x3E /* Inconsistenciy in designated outgoing access information and subscriber class */
+#define CAUSE_Q850_SOONOTAVAIL 0x3F /* Service or option not available, unspecified */
+#define CAUSE_Q850_BCAPNOTIMPL 0x41 /* Bearer capability not implemented */
+#define CAUSE_Q850_CHTYPNIMPL 0x42 /* Channel type not implemented */
+#define CAUSE_Q850_REQFACNIMPL 0x45 /* Requested facility not implemented */
+#define CAUSE_Q850_ORDINBCAVL 0x46 /* Only restricted digital information bearer capability is available */
+#define CAUSE_Q850_SOONOTIMPL 0x4F /* Service or option not implemented, unspecified */
+#define CAUSE_Q850_INVCLRFVAL 0x51 /* Invalid call reference value */
+#define CAUSE_Q850_IDCHDNOEX 0x52 /* Identified channel does not exist */
+#define CAUSE_Q850_SUSCAEXIN 0x53 /* A suspended call exists, but this call identity does not */
+#define CAUSE_Q850_CLIDINUSE 0x54 /* Call identity in use */
+#define CAUSE_Q850_NOCLSUSP 0x55 /* No call suspended */
+#define CAUSE_Q850_CLIDCLRD 0x56 /* Call having the requested call identity has been cleared */
+#define CAUSE_Q850_UNOTMEMCUG 0x57 /* User not member of CUG */
+#define CAUSE_Q850_INCDEST 0x58 /* Incompatible destination */
+#define CAUSE_Q850_NONEXCUG 0x5A /* Non-existent CUG */
+#define CAUSE_Q850_INVNTWSEL 0x5B /* Invalid transit network selection */
+#define CAUSE_Q850_INVMSG 0x5F /* Invalid message, unspecified */
+#define CAUSE_Q850_MIEMISS 0x60 /* Mandatory information element is missing */
+#define CAUSE_Q850_MSGTNI 0x61 /* Message type non-existent or not implemented */
+#define CAUSE_Q850_MSGNCMPT 0x62 /* Message not compatible with call state or message type non-existent or not implemented */
+#define CAUSE_Q850_IENENI 0x63 /* Information element/parameter non-existent or not implemented */
+#define CAUSE_Q850_INVIEC 0x64 /* Invalid information element contents */
+#define CAUSE_Q850_MSGNCWCS 0x65 /* Message not compatible with call state */
+#define CAUSE_Q850_RECOTIMEXP 0x66 /* Recovery on timer expiry */
+#define CAUSE_Q850_PARMNENIPO 0x67 /* Parameter non-existent or not implemented, passed on */
+#define CAUSE_Q850_MSGUNRDPRM 0x6E /* Message with unrecognized parameter, discarded */
+#define CAUSE_Q850_PROTERR 0x6F /* Protocol error, unspecified */
+#define CAUSE_Q850_INTWRKU 0x7F /* Interworking, unspecified */
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/q921.c b/usr.sbin/i4b/isdntrace/q921.c
new file mode 100644
index 0000000..e8c14dc
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q921.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1996 Gary Jennejohn. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ *
+ *---------------------------------------------------------------------------*
+ *
+ * q.921.c - print Q.921 traces
+ * ----------------------------
+ *
+ * $Id: q921.c,v 1.4 1999/12/13 21:25:26 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Dec 13 21:56:46 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode LAPD (Q.921) protocol
+ *---------------------------------------------------------------------------*/
+int
+decode_lapd(char *pbuf, int n, unsigned char *buf, int dir, int raw, int printit)
+{
+ int sap, tei, cmd, p_f;
+ int cnt = 0;
+ int i;
+ char locbuf[32000];
+ char *lbufp = &locbuf[0];
+
+ *lbufp = '\0';
+ *pbuf = '\0';
+
+ sap = (buf[0] >> 2) & 0x3f;
+ cnt++;
+
+ tei = buf[1] >> 1;
+ cnt++;
+
+ if(dir == FROM_TE)
+ cmd = !(buf[0] & 2);
+ else
+ cmd = buf[0] & 2;
+
+ switch (sap)
+ {
+ /* SAPI control procedures */
+ case 0:
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (Call Control), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+
+ if((buf[2] & 0x01) == 0)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "I-Frame: ");
+
+ p_f = buf [3] & 1;
+
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "N(S) %d N(R) %d P %d ", buf [2] >> 1, buf [3] >> 1, p_f);
+
+ cnt += 2;
+ }
+ else if((buf[2] & 0x03) == 0x01)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "S-Frame: ");
+
+ p_f = buf [3] & 1;
+ cmd = buf [2] & 0x0c;
+
+ if(printit)
+ {
+ if (cmd == 0)
+ sprintf((lbufp+strlen(lbufp)), "RR N(R) %d PF %d ", buf [3] >> 1, p_f);
+ if (cmd == 4)
+ sprintf((lbufp+strlen(lbufp)), "RNR N(R) %d PF %d ", buf [3] >> 1, p_f);
+ if (cmd == 8)
+ sprintf((lbufp+strlen(lbufp)), "REJ N(R) %d PF %d ", buf [3] >> 1, p_f);
+ }
+ cnt += 2;
+ }
+ else if((buf[2] & 0x03) == 0x03)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "U-Frame: ");
+
+ p_f = (buf [2] & 0x10) >> 4;
+ cmd = buf [2] & 0xec;
+
+ if(printit)
+ {
+ if (cmd == 0x6c)
+ sprintf((lbufp+strlen(lbufp)), "SABME PF %d ", p_f);
+ if (cmd == 0x0c)
+ sprintf((lbufp+strlen(lbufp)), "DM PF %d ", p_f);
+ if (cmd == 0)
+ sprintf((lbufp+strlen(lbufp)), "UI PF %d ", p_f);
+ if (cmd == 0x40)
+ sprintf((lbufp+strlen(lbufp)), "DISC PF %d ", p_f);
+ if (cmd == 0x60)
+ sprintf((lbufp+strlen(lbufp)), "UA PF %d ", p_f);
+ if (cmd == 0x84)
+ sprintf((lbufp+strlen(lbufp)), "FRMR PF %d ", p_f);
+ if (cmd == 0xac)
+ sprintf((lbufp+strlen(lbufp)), "XID PF %d ", p_f);
+ /* information field ??? */
+ }
+ cnt++;
+ }
+ break;
+ }
+
+ /* D channel X.25 */
+
+ case 16:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (X.25), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+ cnt = n;
+ goto dump;
+
+ /* Loopback test */
+
+ case 32:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (Loopbacktest), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+ cnt = n;
+ goto dump;
+
+ /* SAPI layer 2 management functions */
+
+ case 63:
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: SAP=%d (TEI-Management), %c, TEI=%d, ", sap, cmd?'C':'R', tei);
+
+ if (tei != 127)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "ILLEGAL TEI\n");
+ cnt = n;
+ goto dump;
+ }
+
+ if (buf [2] != 3 && buf [3] != 0xf)
+ {
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "invalid format!\n");
+ cnt = n;
+ goto dump;
+ }
+ cnt+= 2; /* UI + MEI */
+
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Ri=0x%04hx, ", *(short *)&buf[4]);
+ cnt += 2; /* Ri */
+
+ switch (buf[6])
+ {
+ case 1:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdRequest, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 2:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdAssign, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 3:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdDenied, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 4:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdCheckReq, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 5:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdCheckResp, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 6:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdRemove, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ case 7:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "IdVerify, Ai=%d", (buf [7] >> 1));
+ cnt += 2;
+ break;
+ default:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Unknown Msg Type\n");
+ cnt = n;
+ goto dump;
+ }
+ break;
+ }
+
+ /* Illegal SAPI */
+
+ default:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "Q921: ERROR, SAP=%d (Illegal SAPI), %c, TEI=%d\n", sap, cmd?'C':'R', tei);
+ cnt = n;
+ goto dump;
+ }
+
+dump:
+ if(printit)
+ sprintf((lbufp+strlen(lbufp)), "\n");
+
+ if(raw && printit)
+ {
+ int j;
+ for (i = 0; i < cnt; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i);
+ for (j = 0; j < 16; j++)
+ if (i + j < cnt)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < cnt; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"%s", &locbuf[0]);
+
+ return (cnt);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/q931.c b/usr.sbin/i4b/isdntrace/q931.c
new file mode 100644
index 0000000..d69453e
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q931.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q931.c - print Q.931 traces
+ * ---------------------------
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Wed Oct 17 14:49:16 2001]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931 protocol
+ *---------------------------------------------------------------------------*/
+void
+decode_q931(char *pbuf, int n, int off, unsigned char *buf, int raw)
+{
+ int codeset = 0;
+ int codelock = 0;
+ int oldcodeset = 0;
+
+ int pd;
+ int len;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ if(raw)
+ {
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i+off);
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ i = 0;
+
+ sprintf((pbuf+strlen(pbuf)), "Q931: ");
+
+ /* protocol discriminator */
+
+ pd = buf[i];
+
+ if(pd >= 0x00 && pd <= 0x07)
+ sprintf((pbuf+strlen(pbuf)), "pd=User-User (0x%02x)\n",pd);
+ else if(pd == 0x08)
+ sprintf((pbuf+strlen(pbuf)), "pd=Q.931/I.451, ");
+ else if(pd >= 0x10 && pd <= 0x3f)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)\n",pd);
+ else if(pd >= 0x40 && pd <= 0x4f)
+ sprintf((pbuf+strlen(pbuf)), "pd=National Use (0x%02x)\n",pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)\n",pd);
+ else
+ sprintf((pbuf+strlen(pbuf)), "pd=Reserved (0x%02x)\n",pd);
+
+ /* call reference */
+
+ i++;
+
+ len = buf[i] & 0x0f;
+
+ switch(len)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "cr=Dummy, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "cr=0x%02x %s, ", (buf[i+1] & 0x7f), (buf[i+1] & 0x80) ? "(from destination)" : "(from origination)");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "cr=0x%02x 0x%02x %s, ", (buf[i+1] & 0x7f), (buf[i+2] & 0x7f), (buf[i+1] & 0x80) ? "(org)" : "(dst)");
+ break;
+ }
+
+ i += (len+1);
+
+ /* message type */
+
+ sprintf((pbuf+strlen(pbuf)), "message=");
+
+ switch(buf[i])
+ {
+ /* escape to nationally specific message type */
+
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "ESCAPE: ");
+ break;
+
+ /* call establishment */
+
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "ALERTING: ");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "CALL PROCEEDING: ");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "PROGRESS: ");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "SETUP: ");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT: ");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "SETUP ACKNOWLEDGE: ");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "CONNECT ACKNOWLEDGE: ");
+ break;
+
+ /* call information phase */
+
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "USER INFORMATION: ");
+ break;
+ case 0x21:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND REJECT: ");
+ break;
+ case 0x22:
+ sprintf((pbuf+strlen(pbuf)), "RESUME REJECT: ");
+ break;
+ case 0x24:
+ sprintf((pbuf+strlen(pbuf)), "HOLD: ");
+ break;
+ case 0x25:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND: ");
+ break;
+ case 0x26:
+ sprintf((pbuf+strlen(pbuf)), "RESUME: ");
+ break;
+ case 0x28:
+ sprintf((pbuf+strlen(pbuf)), "HOLD ACKNOWLEDGE: ");
+ break;
+ case 0x2d:
+ sprintf((pbuf+strlen(pbuf)), "SUSPEND ACKNOWLEDGE: ");
+ break;
+ case 0x2e:
+ sprintf((pbuf+strlen(pbuf)), "RESUME ACKNOWLEDGE: ");
+ break;
+ case 0x30:
+ sprintf((pbuf+strlen(pbuf)), "HOLD REJECT (Q.932): ");
+ break;
+ case 0x31:
+ sprintf((pbuf+strlen(pbuf)), "RETRIEVE (Q.932): ");
+ break;
+ case 0x32:
+ sprintf((pbuf+strlen(pbuf)), "RETRIEVE ACKNOWLEDGE (Q.932): ");
+ break;
+ case 0x37:
+ sprintf((pbuf+strlen(pbuf)), "RETRIEVE REJECT (Q.932): ");
+ break;
+
+ /* call clearing */
+
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "DETACH: ");
+ break;
+ case 0x45:
+ sprintf((pbuf+strlen(pbuf)), "DISCONNECT: ");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "RESTART: ");
+ break;
+ case 0x48:
+ sprintf((pbuf+strlen(pbuf)), "DETACH ACKNOWLEDGE: ");
+ break;
+ case 0x4d:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE: ");
+ break;
+ case 0x4e:
+ sprintf((pbuf+strlen(pbuf)), "RESTART ACKNOWLEDGE: ");
+ break;
+ case 0x5a:
+ sprintf((pbuf+strlen(pbuf)), "RELEASE COMPLETE: ");
+ break;
+
+ /* misc messages */
+
+ case 0x60:
+ sprintf((pbuf+strlen(pbuf)), "SEGMENT: ");
+ break;
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY (Q.932): ");
+ break;
+ case 0x64:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER (Q.932): ");
+ break;
+ case 0x68:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL ACKNOWLEDGE: ");
+ break;
+ case 0x6a:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY ACKNOWLEDGE: ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER ACKNOWLEDGE: ");
+ break;
+ case 0x6e:
+ sprintf((pbuf+strlen(pbuf)), "NOTIFY: ");
+ break;
+ case 0x70:
+ sprintf((pbuf+strlen(pbuf)), "CANCEL REJECT: ");
+ break;
+ case 0x72:
+ sprintf((pbuf+strlen(pbuf)), "FACILITY REJECT: ");
+ break;
+ case 0x74:
+ sprintf((pbuf+strlen(pbuf)), "REGISTER REJECT: ");
+ break;
+ case 0x75:
+ sprintf((pbuf+strlen(pbuf)), "STATUS ENQIRY: ");
+ break;
+ case 0x79:
+ sprintf((pbuf+strlen(pbuf)), "CONGESTION CONTROL: ");
+ break;
+ case 0x7b:
+ sprintf((pbuf+strlen(pbuf)), "INFORMATION: ");
+ break;
+ case 0x7d:
+ sprintf((pbuf+strlen(pbuf)), "STATUS: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "UNDEFINED, TYPE=0x%02x, ", buf[i]);
+ break;
+ }
+
+ /* other information elements */
+
+ i++;
+
+ for (; i < n;)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n ");
+
+ if(buf[i] & 0x80)
+ {
+ /* single octett info element */
+
+ switch(buf[i] & 0x70)
+ {
+ case 0x00: /* reserved */
+ sprintf((pbuf+strlen(pbuf)), "[reserved single octett info]");
+ break;
+
+ case 0x10: /* shift */
+ oldcodeset = codeset;
+ codeset = buf[i] & 0x07;
+ if(buf[i] & 0x08)
+ codelock = 0;
+ else
+ codelock = 1;
+ sprintf((pbuf+strlen(pbuf)), "[shift: codeset=%d lock=%d]", codeset, codelock);
+ break;
+
+ case 0x20: /* more data */
+ if(buf[i] & 0x01)
+ sprintf((pbuf+strlen(pbuf)), "[sending complete]");
+ else
+ sprintf((pbuf+strlen(pbuf)), "[more data]");
+ break;
+
+ case 0x30: /* congestion level */
+ sprintf((pbuf+strlen(pbuf)), "[congestion level=");
+ switch(buf[i] & 0x0f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "rx-ready]");
+ break;
+ case 0x0f:
+ sprintf((pbuf+strlen(pbuf)), "rx-not-ready]");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)]", buf[i] & 0x0f);
+ break;
+ }
+ break;
+
+ case 0x50: /* repeat ind */
+ sprintf((pbuf+strlen(pbuf)), "[repeat indicator]");
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN SINGLE OCTET ELEMENT 0x%02x]", buf[i]);
+ break;
+ }
+
+ i++; /* next */
+
+ }
+ else
+ {
+ /* variable length info element */
+
+ if(codeset == 0)
+ {
+ switch(buf[i])
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "[segmented message: ");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "[bearer capability: ");
+ i += p_q931bc(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "[cause: ");
+ i += p_q931cause(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x0c:
+ sprintf((pbuf+strlen(pbuf)), "[connected address (old): ");
+ break;
+ case 0x0d:
+ sprintf((pbuf+strlen(pbuf)), "[extended facility (Q.932: )");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "[call identity: ");
+ break;
+ case 0x14:
+ sprintf((pbuf+strlen(pbuf)), "[call state: ");
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Special");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", State=");
+
+ switch((buf[i] & 0x3f))
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "Null");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Call initiated");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "Overlap sending");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Outgoing call proceeding");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "Call delivered");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "Call present");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "Call received");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "Connect request");
+ break;
+ case 9:
+ sprintf((pbuf+strlen(pbuf)), "Incoming call proceeding");
+ break;
+ case 10:
+ sprintf((pbuf+strlen(pbuf)), "Active");
+ break;
+ case 11:
+ sprintf((pbuf+strlen(pbuf)), "Disconnect request");
+ break;
+ case 12:
+ sprintf((pbuf+strlen(pbuf)), "Disconnect indication");
+ break;
+ case 15:
+ sprintf((pbuf+strlen(pbuf)), "Suspend request");
+ break;
+ case 17:
+ sprintf((pbuf+strlen(pbuf)), "Resume request");
+ break;
+ case 19:
+ sprintf((pbuf+strlen(pbuf)), "Release request");
+ break;
+ case 22:
+ sprintf((pbuf+strlen(pbuf)), "Call abort");
+ break;
+ case 25:
+ sprintf((pbuf+strlen(pbuf)), "Overlap receiving");
+ break;
+ case 0x3d:
+ sprintf((pbuf+strlen(pbuf)), "Restart request");
+ break;
+ case 0x3e:
+ sprintf((pbuf+strlen(pbuf)), "Restart");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+ goto next;
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "[channel id: channel=");
+ i++;
+ len = buf[i];
+ i++;
+ switch(buf[i] & 0x03)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "no channel");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "B-1");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "B-2");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "any channel");
+ break;
+ }
+ if(buf[i] & 0x08)
+ sprintf((pbuf+strlen(pbuf)), " (exclusive)]");
+ else
+ sprintf((pbuf+strlen(pbuf)), " (preferred)]");
+ i++;
+ goto next;
+ break;
+ case 0x19:
+ sprintf((pbuf+strlen(pbuf)), "[data link connection id (Q.933): ");
+ break;
+ case 0x1c:
+ i += q932_facility(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x1e:
+ sprintf((pbuf+strlen(pbuf)), "[progress ind: ");
+ i++;
+ len = buf[i];
+ i++;
+ sprintf((pbuf+strlen(pbuf)), "Std=");
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Local");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), ", Loc=");
+
+ switch((buf[i] & 0x0f))
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "User");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Private network serving local user");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "Public network serving local user");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Transit network");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "Public network serving remote user");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "Private network serving remote user");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "Network beyond interworking point");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: undefined/reserved");
+ break;
+ }
+
+ i++;
+
+ sprintf((pbuf+strlen(pbuf)), "\n Description: ");
+
+ switch((buf[i] & 0x7f))
+ {
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "Call is not end-to-end ISDN");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "Destination address is non-ISDN");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Origination address is non-ISDN");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "Call has returned to the ISDN");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "Interworking occured, Service change");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "In-band info or appropriate pattern now available");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "ERROR: undefined/reserved");
+ break;
+ }
+ sprintf((pbuf+strlen(pbuf)), "]");
+ i++;
+ goto next;
+ break;
+ case 0x20:
+ sprintf((pbuf+strlen(pbuf)), "[network specific facilities: ");
+ break;
+ case 0x24:
+ sprintf((pbuf+strlen(pbuf)), "[terminal capabilities: ");
+ break;
+ case 0x27:
+ sprintf((pbuf+strlen(pbuf)), "[notification indicator: ");
+ i += p_q931notification(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x28:
+ sprintf((pbuf+strlen(pbuf)), "[display: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x29:
+ sprintf((pbuf+strlen(pbuf)), "[date/time: ");
+ i++;
+ len = buf[i];
+ i++;
+ j = 0;
+ sprintf((pbuf+strlen(pbuf)),"%.2d.%.2d.%.2d",
+ buf[i+2], buf[i+1], buf[i]);
+ j+=3;
+ if(j < len)
+ {
+ sprintf((pbuf+strlen(pbuf))," %.2d", buf[i+3]);
+ j++;
+ }
+ if(j < len)
+ {
+ sprintf((pbuf+strlen(pbuf)),":%.2d", buf[i+4]);
+ j++;
+ }
+ if(j < len)
+ {
+ sprintf((pbuf+strlen(pbuf)),":%.2d", buf[i+5]);
+ j++;
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += len;
+ goto next;
+ break;
+ case 0x2c:
+ sprintf((pbuf+strlen(pbuf)), "[keypad: ");
+ i++;
+ len = buf[i];
+ i++;
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+ goto next;
+ break;
+ case 0x30:
+ sprintf((pbuf+strlen(pbuf)), "[keypad echo: ");
+ break;
+ case 0x32:
+ sprintf((pbuf+strlen(pbuf)), "[information req (Q.932): ");
+ break;
+ case 0x34:
+ sprintf((pbuf+strlen(pbuf)), "[signal: ");
+ break;
+ case 0x36:
+ sprintf((pbuf+strlen(pbuf)), "[switchhook: ");
+ break;
+ case 0x38:
+ sprintf((pbuf+strlen(pbuf)), "[feature activation (Q.932): ");
+ break;
+ case 0x39:
+ sprintf((pbuf+strlen(pbuf)), "[feature ind (Q.932): ");
+ break;
+ case 0x3a:
+ sprintf((pbuf+strlen(pbuf)), "[service profile id (Q.932): ");
+ break;
+ case 0x3b:
+ sprintf((pbuf+strlen(pbuf)), "[endpoint id (Q.932): ");
+ break;
+ case 0x40:
+ sprintf((pbuf+strlen(pbuf)), "[information rate: ");
+ break;
+ case 0x41:
+ sprintf((pbuf+strlen(pbuf)), "[precedence level (Q.955): ");
+ break;
+ case 0x42:
+ sprintf((pbuf+strlen(pbuf)), "[end-to-end transit delay: ");
+ break;
+ case 0x43:
+ sprintf((pbuf+strlen(pbuf)), "[transit delay detection and indication: ");
+ break;
+ case 0x44:
+ sprintf((pbuf+strlen(pbuf)), "[packet layer binary parameters: ");
+ break;
+ case 0x45:
+ sprintf((pbuf+strlen(pbuf)), "[packet layer window size: ");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "[packet size: ");
+ break;
+ case 0x47:
+ sprintf((pbuf+strlen(pbuf)), "[closed user group: ");
+ break;
+ case 0x48:
+ sprintf((pbuf+strlen(pbuf)), "[link layer core parameters (Q.933): ");
+ break;
+ case 0x49:
+ sprintf((pbuf+strlen(pbuf)), "[link layer protocol parameters (Q.933): ");
+ break;
+ case 0x4a:
+ sprintf((pbuf+strlen(pbuf)), "[reverse charging information: ");
+ break;
+ case 0x4c:
+ sprintf((pbuf+strlen(pbuf)), "[connected number (Q.951): ");
+ i += p_q931address(pbuf, &buf[i]);
+ goto next;
+ break;
+
+ break;
+ case 0x4d:
+ sprintf((pbuf+strlen(pbuf)), "[connected subaddress (Q.951): ");
+ break;
+ case 0x50:
+ sprintf((pbuf+strlen(pbuf)), "[X.213 priority (Q.933): ");
+ break;
+ case 0x51:
+ sprintf((pbuf+strlen(pbuf)), "[report type (Q.933): ");
+ break;
+ case 0x53:
+ sprintf((pbuf+strlen(pbuf)), "[link integrity verification (Q.933): ");
+ break;
+ case 0x57:
+ sprintf((pbuf+strlen(pbuf)), "[PVC status (Q.933): ");
+ break;
+ case 0x6c:
+ sprintf((pbuf+strlen(pbuf)), "[calling party number: ");
+ i += p_q931address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x6d:
+ sprintf((pbuf+strlen(pbuf)), "[calling party subaddress: ");
+ break;
+ case 0x70:
+ sprintf((pbuf+strlen(pbuf)), "[called party number: ");
+ i += p_q931address(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x71:
+ sprintf((pbuf+strlen(pbuf)), "[called party subaddress: ");
+ break;
+ case 0x74:
+ sprintf((pbuf+strlen(pbuf)), "[redirecting number: ");
+ i += p_q931redir(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x76:
+ sprintf((pbuf+strlen(pbuf)), "[redirection number: ");
+ i += p_q931redir(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x78:
+ sprintf((pbuf+strlen(pbuf)), "[transit network selection: ");
+ break;
+ case 0x79:
+ sprintf((pbuf+strlen(pbuf)), "[restart indicator: ");
+ break;
+ case 0x7c:
+ sprintf((pbuf+strlen(pbuf)), "[low layer compatibility: ");
+ break;
+ case 0x7d:
+ sprintf((pbuf+strlen(pbuf)), "[high layer compatibility:");
+ i += p_q931high_compat(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x7e:
+ sprintf((pbuf+strlen(pbuf)), "[user-user: ");
+ i += p_q931user_user(pbuf, &buf[i]);
+ goto next;
+ break;
+ case 0x7f:
+ sprintf((pbuf+strlen(pbuf)), "[escape for extension: ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN INFO-ELEMENT-ID=0x%02x: ", buf[i]);
+ break;
+ }
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "[UNKNOWN CODESET=%d, IE=0x%02x: ", codeset, buf[i]);
+ }
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ sprintf((pbuf+strlen(pbuf)), "LEN=0x%02x, DATA=", len);
+
+ i++; /* index -> 1st param */
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"0x%02x ", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += len;
+
+next:
+
+ if(!codelock && (codeset != oldcodeset))
+ codeset = oldcodeset;
+ }
+ }
+ sprintf((pbuf+strlen(pbuf)),"\n");
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/q931_util.c b/usr.sbin/i4b/isdntrace/q931_util.c
new file mode 100644
index 0000000..9f82283
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q931_util.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q931_util.c - utility functions to print Q.931 traces
+ * -----------------------------------------------------
+ *
+ * $Id: q931_util.c,v 1.11 2000/02/15 12:48:14 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Tue Feb 15 13:52:09 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode and print the cause
+ *---------------------------------------------------------------------------*/
+int
+p_q931cause(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int ls;
+ int r = 0;
+ int rflag = 0;
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++; /* coding/location */
+ len--;
+
+ ls = buf[i];
+
+ i++;
+ len--;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ r = buf[i];
+ rflag = 1;
+ i++;
+ len--;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "%s ", print_cause_q850(buf[i] & 0x7f));
+
+ sprintf((pbuf+strlen(pbuf)), "\n (location=");
+
+ switch(ls & 0x0f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "user");
+ break;
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "private network serving local user");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "public network serving local user");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "transit network");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "public network serving remote user");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "private network serving remote user");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "international network");
+ break;
+ case 0x0a:
+ sprintf((pbuf+strlen(pbuf)), "network beyond interworking point");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", ls & 0x0f);
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), ", std=");
+
+ switch((ls & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "Local");
+ break;
+ }
+
+ if(rflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ", rec=");
+
+ switch(r & 0x7f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "Q.931");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "X.21");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "X.25");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "Q.1031/Q.1051");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "Reserved");
+ break;
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")");
+
+ i++;
+ len--;
+
+ for(j = 0; j < len; j++)
+ sprintf((pbuf+strlen(pbuf))," 0x%02x", buf[j+i]);
+
+ sprintf((pbuf+strlen(pbuf)),"]");
+
+ i += (len+1);
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the bearer capability
+ *---------------------------------------------------------------------------*/
+int
+p_q931bc(char *pbuf, unsigned char *buf)
+{
+ int len;
+ int i = 0;
+ int mr = 0;
+
+ i++; /* index -> length */
+
+ len = buf[i];
+
+ i++;
+
+ sprintf((pbuf+strlen(pbuf)), "\n cap=");
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "speech");
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "unrestricted digital information");
+ break;
+ case 0x09:
+ sprintf((pbuf+strlen(pbuf)), "restricted digital information");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "3.1 kHz audio");
+ break;
+ case 0x11:
+ sprintf((pbuf+strlen(pbuf)), "unrestricted digital information with tones");
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "video");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "\n std=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "CCITT");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "ISO/IEC");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "National");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "NSI Std");
+ break;
+ }
+
+ i++;
+ len--;
+
+ sprintf((pbuf+strlen(pbuf)), "\n rate=");
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x00:
+ sprintf((pbuf+strlen(pbuf)), "packet mode");
+ break;
+ case 0x10:
+ sprintf((pbuf+strlen(pbuf)), "64 kbit/s");
+ break;
+ case 0x11:
+ sprintf((pbuf+strlen(pbuf)), "2 x 64 kbit/s");
+ break;
+ case 0x13:
+ sprintf((pbuf+strlen(pbuf)), "384 kbit/s");
+ break;
+ case 0x15:
+ sprintf((pbuf+strlen(pbuf)), "1536 kbit/s");
+ break;
+ case 0x17:
+ sprintf((pbuf+strlen(pbuf)), "1920 kbit/s");
+ break;
+ case 0x18:
+ sprintf((pbuf+strlen(pbuf)), "Multirate");
+ mr = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+
+ sprintf((pbuf+strlen(pbuf)), "\n mode=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "circuit");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", ((buf[i] & 0x60) >> 5));
+ break;
+ }
+
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ if(mr)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n rate multiplier=%d", buf[i] & 0x7f);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ sprintf((pbuf+strlen(pbuf)), "\n layer1=");
+
+ switch(buf[i] & 0x1f)
+ {
+ case 0x01:
+ sprintf((pbuf+strlen(pbuf)), "V.110");
+ break;
+ case 0x02:
+ sprintf((pbuf+strlen(pbuf)), "G.711 u-law");
+ break;
+ case 0x03:
+ sprintf((pbuf+strlen(pbuf)), "G.711 A-law");
+ break;
+ case 0x04:
+ sprintf((pbuf+strlen(pbuf)), "G.721");
+ break;
+ case 0x05:
+ sprintf((pbuf+strlen(pbuf)), "H.221/H.242");
+ break;
+ case 0x07:
+ sprintf((pbuf+strlen(pbuf)), "Non-Std");
+ break;
+ case 0x08:
+ sprintf((pbuf+strlen(pbuf)), "V.120");
+ break;
+ case 0x09:
+ sprintf((pbuf+strlen(pbuf)), "X.31");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reserved (0x%02x)", buf[i] & 0x0f);
+ break;
+ }
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n user rate=0x%02x ", buf[i] & 0x1f);
+
+ if(buf[i] & 0x40)
+ sprintf((pbuf+strlen(pbuf)), "(async,");
+ else
+ sprintf((pbuf+strlen(pbuf)), "(sync,");
+
+ if(buf[i] & 0x20)
+ sprintf((pbuf+strlen(pbuf)), "in-band neg. possible)");
+ else
+ sprintf((pbuf+strlen(pbuf)), "in-band neg not possible)");
+
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n clk/flow=0x%02x", buf[i] & 0x1f);
+
+ sprintf((pbuf+strlen(pbuf)), "\n intermediate rate=");
+
+ switch((buf[i] & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "not used");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "8 kbit/s");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "16 kbit/s");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "32 kbit/s");
+ break;
+ }
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n hdr/mfrm/etc.=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n stop/data/parity=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ if(!(buf[i-1] & 0x80))
+ {
+ sprintf((pbuf+strlen(pbuf)), "\n modemtype=0x%02x", buf[i]);
+ i++;
+ len--;
+ }
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x42:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=Q.921/I.441");
+ break;
+ case 0x46:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=X.25 link");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer2=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+ if(!len)
+ goto exit;
+
+ switch(buf[i] & 0x7f)
+ {
+ case 0x62:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=Q.921/I.441");
+ break;
+ case 0x66:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=X.25 packet");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\n layer3=0x%02x",(buf[i] & 0x7f));
+ break;
+ }
+ i++;
+ len--;
+
+exit:
+ sprintf((pbuf+strlen(pbuf)), "]");
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print the ISDN (telephone) number
+ *---------------------------------------------------------------------------*/
+int
+p_q931address(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+ int ind = 0;
+ int indflag = 0;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> type/plan */
+ tp = buf[i];
+
+ i++;
+ len--;
+
+ if(!(tp & 0x80))
+ {
+ ind = buf[i];
+ indflag = 1;
+ i++;
+ len--;
+ }
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " (type=unknown, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " (type=international, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " (type=national, ");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " (type=network specific, ");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), " (type=subscriber, ");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), " (type=abbreviated, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " (type=reserved (%d), ", ((tp & 0x70) >> 4));
+ break;
+ }
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "plan=unknown");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "plan=ISDN");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "plan=Data");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "plan=Telex");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "plan=National");
+ break;
+ case 9:
+ sprintf((pbuf+strlen(pbuf)), "plan=private");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "plan=reserved (%d)", (tp & 0x0f));
+ break;
+ }
+
+ if(indflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ",\n ");
+ switch((ind & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "presentation allowed, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "presentation restricted, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "number not available, ");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "reserved, ");
+ break;
+ }
+
+ switch(ind & 0x03)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "screening user provided: not screened");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "screening user provided: verified & passed");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "screening user provided: verified & failed");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "screening network provided");
+ break;
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")]");
+
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print HL comatibility
+ *---------------------------------------------------------------------------*/
+int
+p_q931high_compat(char *pbuf, unsigned char *buf)
+{
+ int len = buf[1];
+
+ sprintf(pbuf+strlen(pbuf), " standard=");
+
+ switch ((buf[2] >> 5) & 0x03)
+ {
+ case 0: sprintf(pbuf+strlen(pbuf), "CCITT");
+ break;
+ case 1: sprintf(pbuf+strlen(pbuf), "unknown international standard");
+ break;
+ case 2: sprintf(pbuf+strlen(pbuf), "unknown national standard");
+ break;
+ case 3: sprintf(pbuf+strlen(pbuf), "local network standard");
+ }
+
+ len--;
+
+ sprintf(pbuf+strlen(pbuf), ", characteristics=");
+
+ switch (buf[3] & 0x7f)
+ {
+ case 0x01:
+ sprintf(pbuf+strlen(pbuf), "Telephony");
+ break;
+ case 0x04:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 2/3");
+ break;
+ case 0x21:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 4 Class I (F.184)");
+ break;
+ case 0x24:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/mixed (F.230) or Fax Group 4 Class II/III (F.184)");
+ break;
+ case 0x28:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/processable (F.220)");
+ break;
+ case 0x31:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic mode (F.200)");
+ break;
+ case 0x32:
+ sprintf(pbuf+strlen(pbuf), "Videotex (F.300 and T.101)");
+ break;
+ case 0x35:
+ sprintf(pbuf+strlen(pbuf), "Telex (F.60)");
+ break;
+ case 0x38:
+ sprintf(pbuf+strlen(pbuf), "MHS (X.400 series)");
+ break;
+ case 0x41:
+ sprintf(pbuf+strlen(pbuf), "OSI application (X.200 series)");
+ break;
+ case 0x5e:
+ sprintf(pbuf+strlen(pbuf), "Maintenance");
+ break;
+ case 0x5f:
+ sprintf(pbuf+strlen(pbuf), "Management");
+ break;
+ case 0x7f:
+ sprintf(pbuf+strlen(pbuf), "reserved");
+ break;
+ default:
+ sprintf(pbuf+strlen(pbuf), "UNKNOWN (0x%02x)", buf[3]);
+ break;
+ }
+
+ len--;
+
+ if (!len)
+ {
+ sprintf(pbuf+strlen(pbuf), "]");
+ return 4;
+ }
+
+ sprintf(pbuf+strlen(pbuf), " of ");
+
+ switch (buf[4] & 0x7f)
+ {
+ case 0x01:
+ sprintf(pbuf+strlen(pbuf), "Telephony");
+ break;
+ case 0x04:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 2/3");
+ break;
+ case 0x21:
+ sprintf(pbuf+strlen(pbuf), "Fax Group 4 Class I (F.184)");
+ break;
+ case 0x24:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/mixed (F.230) or Fax Group 4 Class II/III (F.184)");
+ break;
+ case 0x28:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic/processable (F.220)");
+ break;
+ case 0x31:
+ sprintf(pbuf+strlen(pbuf), "Teletex basic mode (F.200)");
+ break;
+ case 0x32:
+ sprintf(pbuf+strlen(pbuf), "Videotex (F.300 and T.101)");
+ break;
+ case 0x35:
+ sprintf(pbuf+strlen(pbuf), "Telex (F.60)");
+ break;
+ case 0x38:
+ sprintf(pbuf+strlen(pbuf), "MHS (X.400 series)");
+ break;
+ case 0x41:
+ sprintf(pbuf+strlen(pbuf), "OSI application (X.200 series)");
+ break;
+ case 0x7f:
+ sprintf(pbuf+strlen(pbuf), "reserved");
+ break;
+ default:
+ sprintf(pbuf+strlen(pbuf), "UNKNOWN (0x%02x)", buf[3]);
+ break;
+ }
+ sprintf(pbuf+strlen(pbuf), "]");
+ return 5;
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print user-user IE
+ *---------------------------------------------------------------------------*/
+int
+p_q931user_user(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int pd;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> protocoldiscriminator */
+ pd = buf[i];
+
+ switch(pd)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "(pd=user-specific");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "(pd=OSI high-layer protocols");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "(pd=X.244");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "(pd=reserved for system management");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "(pd=IA5 characters");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "(pd=X.208/X.209 coded user info");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "(pd=V.120 rate adaption");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "(pd=Q.931/I.451 user network call control messages");
+ break;
+ default:
+ if(pd >= 0x10 && pd <= 0x3f)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved for other L3 protocols incl. X.25", pd);
+ else if(pd >= 0x40 && pd <= 0x47)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=national use", pd);
+ else if(pd >= 0x48 && pd <= 0x4f)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved for ETSI", pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved for other L3 protocols incl. X.25", pd);
+ else
+ sprintf((pbuf+strlen(pbuf)), "(pd=0x%x=reserved", pd);
+ break;
+ }
+
+ i++;
+ len--;
+
+ sprintf((pbuf+strlen(pbuf)),": (");
+
+ for(j = 0; j < len; j++)
+ {
+ if(pd == 4)
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ else
+ sprintf((pbuf+strlen(pbuf)),"0x%2x", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"))]");
+
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and notification indicator IE (Q.932, p44)
+ *---------------------------------------------------------------------------*/
+int
+p_q931notification(char *pbuf, unsigned char *buf)
+{
+ int j = 0;
+ int len;
+ int i = 0;
+ int nd;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> notification description */
+ nd = buf[i];
+
+ switch(nd)
+ {
+ case 0x80:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, user suspended", nd);
+ break;
+ case 0x81:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, user resumed", nd);
+ break;
+ case 0x82:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, bearer service changed", nd);
+ break;
+
+ case 0x83:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, BER coded information", nd);
+ break;
+
+ case 0xc2:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference established", nd);
+ break;
+ case 0xc3:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference disconnected", nd);
+ break;
+ case 0xc4:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party added", nd);
+ break;
+ case 0xc5:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, isolated", nd);
+ break;
+ case 0xc6:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, reattached", nd);
+ break;
+ case 0xc7:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party isolated", nd);
+ break;
+ case 0xc8:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party reattached", nd);
+ break;
+ case 0xc9:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party split", nd);
+ break;
+ case 0xca:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, other party disconnected", nd);
+ break;
+ case 0xcb:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference floating", nd);
+ break;
+ case 0xcc:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference disconnected, preemption", nd);
+ break;
+ case 0xcf:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, conference floating, server user preempted", nd);
+ break;
+
+ case 0xe0:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call is a waiting call", nd);
+ break;
+ case 0xe8:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, diversion activated", nd);
+ break;
+ case 0xe9: /* ECT, EN 300 369-1 V1.2.4 p12) */
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call transferred, alerting", nd);
+ break;
+ case 0xea: /* ECT, EN 300 369-1 V1.2.4 p12) */
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call transferred, active", nd);
+ break;
+ case 0xee:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, reverse charging", nd);
+ break;
+
+ case 0xf9:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, remote hold", nd);
+ break;
+ case 0xfa:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, remote retrieval", nd);
+ break;
+ case 0xfb:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, call is diverting", nd);
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "0x%2x, undefined", nd);
+ break;
+ }
+
+ i++;
+ len--;
+
+ if(len)
+ {
+ sprintf((pbuf+strlen(pbuf)),": (");
+
+ for(; j < len; j++)
+ {
+ if(nd == 4)
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ else
+ sprintf((pbuf+strlen(pbuf)),"0x%2x", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")");
+ }
+ sprintf((pbuf+strlen(pbuf)),"]");
+ i += j;
+
+ return(i);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode and print redirecting/redirection number
+ *---------------------------------------------------------------------------*/
+int
+p_q931redir(char *pbuf, unsigned char *buf)
+{
+ int j;
+ int len;
+ int i = 0;
+ int tp;
+ int ind = 0;
+ int indflag = 0;
+ int reas = 0;
+ int reasflag = 0;
+
+ i++; /* index -> length */
+ len = buf[i];
+
+ i++; /* index -> type/plan */
+ tp = buf[i];
+
+ i++;
+ len--;
+
+ if(!(tp & 0x80))
+ {
+ ind = buf[i];
+ indflag = 1;
+ i++;
+ len--;
+
+ if(!(ind & 0x80))
+ {
+ reas = buf[i];
+ reasflag = 1;
+ i++;
+ len--;
+ }
+ }
+
+ for(j = 0; j < len; j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[j+i]);
+ }
+
+ switch((tp & 0x70) >> 4)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " (type=unknown, ");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " (type=international, ");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " (type=national, ");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " (type=network specific, ");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), " (type=subscriber, ");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), " (type=abbreviated, ");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " (type=reserved (%d), ", ((tp & 0x70) >> 4));
+ break;
+ }
+
+ switch(tp & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "plan=unknown");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "plan=ISDN");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "plan=Data");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "plan=Telex");
+ break;
+ case 8:
+ sprintf((pbuf+strlen(pbuf)), "plan=National");
+ break;
+ case 9:
+ sprintf((pbuf+strlen(pbuf)), "plan=private");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "plan=reserved (%d)", (tp & 0x0f));
+ break;
+ }
+
+ if(indflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ",\n ");
+ switch((ind & 0x60) >> 5)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "presentation allowed");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "presentation restricted");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "number not available");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "reserved");
+ break;
+ }
+ }
+
+ if(reasflag)
+ {
+ sprintf((pbuf+strlen(pbuf)), ",\n ");
+ switch(reas & 0x0f)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: unknown");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: call forwarding busy");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: call forwarding unconditional");
+ break;
+ case 0xa:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: called DTE");
+ break;
+ case 0xf:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: call forwarding unconditional");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "reason for diversion: reserved (0x%2x)",reas & 0x0f);
+ break;
+ }
+ }
+
+ sprintf((pbuf+strlen(pbuf)),")]");
+
+ i += j;
+
+ return(i);
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/q932_fac.c b/usr.sbin/i4b/isdntrace/q932_fac.c
new file mode 100644
index 0000000..0a0099d
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q932_fac.c
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q932_fac.c - decode Q.932 facilities
+ * ------------------------------------
+ *
+ * $Id: q932_fac.c,v 1.8 2000/02/24 16:32:46 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Thu Feb 24 17:36:47 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+#include "q932_fac.h"
+
+static int do_component(int length, char *pbuf);
+static char *uni_str(int code);
+static char *opval_str(int val);
+static char *bid_str(int val);
+static void next_state(char *pbuf, int class, int form, int code, int val);
+
+static void object_id(int comp_length, unsigned char *pbuf);
+
+static int byte_len;
+static unsigned char *byte_buf;
+static int state;
+
+/*---------------------------------------------------------------------------*
+ * decode Q.931/Q.932 facility info element
+ *---------------------------------------------------------------------------*/
+int
+q932_facility(char *pbuf, unsigned char *buf)
+{
+ int len;
+
+ sprintf((pbuf+strlen(pbuf)), "[facility (Q.932): ");
+
+ buf++; /* length */
+
+ len = *buf;
+
+ buf++; /* protocol profile */
+
+ sprintf((pbuf+strlen(pbuf)), "Protocol=");
+
+ switch(*buf & 0x1f)
+ {
+ case FAC_PROTO_ROP:
+ sprintf((pbuf+strlen(pbuf)), "Remote Operations Protocol\n");
+ break;
+
+ case FAC_PROTO_CMIP:
+ sprintf((pbuf+strlen(pbuf)), "CMIP Protocol (Q.941), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ case FAC_PROTO_ACSE:
+ sprintf((pbuf+strlen(pbuf)), "ACSE Protocol (X.217/X.227), UNSUPPORTED!\n");
+ return(len+2);
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)), "Unknown Protocol (val = 0x%x), UNSUPPORTED!\n", *buf & 0x1f);
+ return(len+2);
+ break;
+ }
+
+ /* next byte */
+
+ buf++;
+ len--;
+
+ /* initialize variables for do_component */
+
+ byte_len = 0;
+ byte_buf = buf;
+ state = ST_EXP_COMP_TYP;
+
+ /* decode facility */
+
+ do_component(len, pbuf);
+
+ sprintf((pbuf+(strlen(pbuf)-1)), "]"); /* XXX replace last newline */
+
+ return(len+3);
+}
+
+/*---------------------------------------------------------------------------*
+ * handle a component recursively
+ *---------------------------------------------------------------------------*/
+static int
+do_component(int length, char *pbuf)
+{
+ int comp_tag_class; /* component tag class */
+ int comp_tag_form; /* component form: constructor or primitive */
+ int comp_tag_code; /* component code depending on class */
+ int comp_length = 0; /* component length */
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "ENTER - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+again:
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "AGAIN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+
+ /*----------------------------------------*/
+ /* first component element: component tag */
+ /*----------------------------------------*/
+
+ /* tag class bits */
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Tag: ", *byte_buf);
+
+ comp_tag_class = (*byte_buf & 0xc0) >> 6;
+
+ switch(comp_tag_class)
+ {
+ case FAC_TAGCLASS_UNI:
+ sprintf((pbuf+strlen(pbuf)), "Universal");
+ break;
+ case FAC_TAGCLASS_APW:
+ sprintf((pbuf+strlen(pbuf)), "Applic-wide");
+ break;
+ case FAC_TAGCLASS_COS:
+ sprintf((pbuf+strlen(pbuf)), "Context-spec");
+ break;
+ case FAC_TAGCLASS_PRU:
+ sprintf((pbuf+strlen(pbuf)), "Private");
+ break;
+ }
+
+ /* tag form bit */
+
+ comp_tag_form = (*byte_buf & 0x20) > 5;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_form == FAC_TAGFORM_CON)
+ {
+ sprintf((pbuf+strlen(pbuf)), "Constructor");
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "Primitive");
+ }
+
+ /* tag code bits */
+
+ comp_tag_code = *byte_buf & 0x1f;
+
+ sprintf((pbuf+strlen(pbuf)), ", ");
+
+ if(comp_tag_code == 0x1f)
+ {
+ comp_tag_code = 0;
+
+ byte_buf++;
+ byte_len++;
+
+ while(*byte_buf & 0x80)
+ {
+ comp_tag_code += (*byte_buf & 0x7f);
+ byte_buf++;
+ byte_len++;
+ }
+ comp_tag_code += (*byte_buf & 0x7f);
+ sprintf((pbuf+strlen(pbuf)), "%d (ext)\n", comp_tag_code);
+ }
+ else
+ {
+ comp_tag_code = (*byte_buf & 0x1f);
+
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ sprintf((pbuf+strlen(pbuf)), "%s (%d)\n", uni_str(comp_tag_code), comp_tag_code);
+ }
+ else
+ {
+ sprintf((pbuf+strlen(pbuf)), "code = %d\n", comp_tag_code);
+ }
+ }
+
+ byte_buf++;
+ byte_len++;
+
+ /*--------------------------------------------*/
+ /* second component element: component length */
+ /*--------------------------------------------*/
+
+ sprintf((pbuf+strlen(pbuf)), "\t0x%02x Len: ", *byte_buf);
+
+ comp_length = 0;
+
+ if(*byte_buf & 0x80)
+ {
+ int i = *byte_buf & 0x7f;
+
+ byte_len += i;
+
+ for(;i > 0;i++)
+ {
+ byte_buf++;
+ comp_length += (*byte_buf * (i*256));
+ }
+ sprintf((pbuf+strlen(pbuf)), "%d (long form)\n", comp_length);
+ }
+ else
+ {
+ comp_length = *byte_buf & 0x7f;
+ sprintf((pbuf+strlen(pbuf)), "%d (short form)\n", comp_length);
+ }
+
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, -1);
+
+ byte_len++;
+ byte_buf++;
+
+ if(comp_length)
+ {
+
+ /*---------------------------------------------*/
+ /* third component element: component contents */
+ /*---------------------------------------------*/
+
+ if(comp_tag_form) /* == constructor */
+ {
+ do_component(comp_length, pbuf);
+ }
+ else
+ {
+ int val = 0;
+ if(comp_tag_class == FAC_TAGCLASS_UNI)
+ {
+ switch(comp_tag_code)
+ {
+ case FAC_CODEUNI_INT:
+ case FAC_CODEUNI_ENUM:
+ case FAC_CODEUNI_BOOL:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x ", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "Val: %d\n", val);
+ }
+ break;
+
+ case FAC_CODEUNI_OBJI: /* object id */
+
+ if(comp_length)
+ object_id(comp_length, pbuf);
+ break;
+
+ default:
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x = %d", *byte_buf, *byte_buf);
+ if(isprint(*byte_buf))
+ sprintf((pbuf+strlen(pbuf)), " = '%c'", *byte_buf);
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ }
+ break;
+ }
+ }
+
+ else /* comp_tag_class != FAC_TAGCLASS_UNI */
+ {
+ if(comp_length)
+ {
+ int i;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x", *byte_buf);
+ val += (*byte_buf + (i*255));
+ byte_buf++;
+ byte_len++;
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ }
+ sprintf((pbuf+strlen(pbuf)), "\n");
+ }
+ }
+ next_state(pbuf, comp_tag_class, comp_tag_form, comp_tag_code, val);
+ }
+ }
+
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "PREGOTO - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ if(byte_len < length)
+ goto again;
+#ifdef FAC_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "RETURN - comp_length = %d, byte_len = %d, length =%d\n", comp_length, byte_len, length);
+#endif
+ return(byte_len);
+}
+
+/*---------------------------------------------------------------------------*
+ * print universal id type
+ *---------------------------------------------------------------------------*/
+static char *uni_str(int code)
+{
+ static char *tbl[] = {
+ "BOOLEAN",
+ "INTEGER",
+ "BIT STRING",
+ "OCTET STRING",
+ "NULL",
+ "OBJECT IDENTIFIER",
+ "OBJECT DESCRIPTOR",
+ "EXTERNAL",
+ "REAL",
+ "ENUMERATED",
+ "RESERVED11",
+ "RESERVED12",
+ "RESERVED13",
+ "RESERVED14",
+ "RESERVED15",
+ "SEQUENCE",
+ "SET",
+ "NUMERIC STRING",
+ "PRINTABLE STRING",
+ "TELETEX STRING",
+ "ISO646 STRING",
+ "IA5 STRING",
+ "GRAPHIC STRING",
+ "GENERAL STRING"
+ };
+
+ if(code >= 1 && code <= FAC_CODEUNI_GNSTR)
+ return(tbl[code-1]);
+ else
+ return("ERROR, Value out of Range!");
+}
+
+/*---------------------------------------------------------------------------*
+ * print operation value
+ *---------------------------------------------------------------------------*/
+static char *opval_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case FAC_OPVAL_UUS:
+ r = "uUs";
+ break;
+ case FAC_OPVAL_CUG:
+ r = "cUGCall";
+ break;
+ case FAC_OPVAL_MCID:
+ r = "mCIDRequest";
+ break;
+ case FAC_OPVAL_BTPY:
+ r = "beginTPY";
+ break;
+ case FAC_OPVAL_ETPY:
+ r = "endTPY";
+ break;
+ case FAC_OPVAL_ECT:
+ r = "eCTRequest";
+ break;
+ case FAC_OPVAL_DIV_ACT:
+ r = "activationDiversion";
+ break;
+ case FAC_OPVAL_DIV_DEACT:
+ r = "deactivationDiversion";
+ break;
+ case FAC_OPVAL_DIV_ACTSN:
+ r = "activationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_DEACTSN:
+ r = "deactivationStatusNotificationDiv";
+ break;
+ case FAC_OPVAL_DIV_INTER:
+ r = "interrogationDiversion";
+ break;
+ case FAC_OPVAL_DIV_INFO:
+ r = "diversionInformation";
+ break;
+ case FAC_OPVAL_DIV_CALLDEF:
+ r = "callDeflection";
+ break;
+ case FAC_OPVAL_DIV_CALLRER:
+ r = "callRerouting";
+ break;
+ case FAC_OPVAL_DIV_LINF2:
+ r = "divertingLegInformation2";
+ break;
+ case FAC_OPVAL_DIV_INVS:
+ r = "invokeStatus";
+ break;
+ case FAC_OPVAL_DIV_INTER1:
+ r = "interrogationDiversion1";
+ break;
+ case FAC_OPVAL_DIV_LINF1:
+ r = "divertingLegInformation1";
+ break;
+ case FAC_OPVAL_DIV_LINF3:
+ r = "divertingLegInformation3";
+ break;
+ case FAC_OPVAL_ER_CRCO:
+ r = "explicitReservationCreationControl";
+ break;
+ case FAC_OPVAL_ER_MGMT:
+ r = "explicitReservationManagement";
+ break;
+ case FAC_OPVAL_ER_CANC:
+ r = "explicitReservationCancel";
+ break;
+ case FAC_OPVAL_MLPP_QUERY:
+ r = "mLPP lfb Query";
+ break;
+ case FAC_OPVAL_MLPP_CALLR:
+ r = "mLPP Call Request";
+ break;
+ case FAC_OPVAL_MLPP_CALLP:
+ r = "mLPP Call Preemption";
+ break;
+ case FAC_OPVAL_AOC_REQ:
+ r = "chargingRequest";
+ break;
+ case FAC_OPVAL_AOC_S_CUR:
+ r = "aOCSCurrency";
+ break;
+ case FAC_OPVAL_AOC_S_SPC:
+ r = "aOCSSpecialArrangement";
+ break;
+ case FAC_OPVAL_AOC_D_CUR:
+ r = "aOCDCurrency";
+ break;
+ case FAC_OPVAL_AOC_D_UNIT:
+ r = "aOCDChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_E_CUR:
+ r = "aOCECurrency";
+ break;
+ case FAC_OPVAL_AOC_E_UNIT:
+ r = "aOCEChargingUnit";
+ break;
+ case FAC_OPVAL_AOC_IDOFCRG:
+ r = "identificationOfCharge";
+ break;
+ case FAC_OPVAL_CONF_BEG:
+ r = "beginConf";
+ break;
+ case FAC_OPVAL_CONF_ADD:
+ r = "addConf";
+ break;
+ case FAC_OPVAL_CONF_SPLIT:
+ r = "splitConf";
+ break;
+ case FAC_OPVAL_CONF_DROP:
+ r = "dropConf";
+ break;
+ case FAC_OPVAL_CONF_ISOLATE:
+ r = "isolateConf";
+ break;
+ case FAC_OPVAL_CONF_REATT:
+ r = "reattachConf";
+ break;
+ case FAC_OPVAL_CONF_PDISC:
+ r = "partyDISC";
+ break;
+ case FAC_OPVAL_CONF_FCONF:
+ r = "floatConf";
+ break;
+ case FAC_OPVAL_CONF_END:
+ r = "endConf";
+ break;
+ case FAC_OPVAL_CONF_IDCFE:
+ r = "indentifyConferee";
+ break;
+ case FAC_OPVAL_REVC_REQ:
+ r = "requestREV";
+ break;
+ default:
+ sprintf(buffer, "unknown operation value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * billing id string
+ *---------------------------------------------------------------------------*/
+static char *bid_str(int val)
+{
+ static char buffer[80];
+ char *r;
+
+ switch(val)
+ {
+ case 0:
+ r = "normalCharging";
+ break;
+ case 1:
+ r = "reverseCharging";
+ break;
+ case 2:
+ r = "creditCardCharging";
+ break;
+ case 3:
+ r = "callForwardingUnconditional";
+ break;
+ case 4:
+ r = "callForwardingBusy";
+ break;
+ case 5:
+ r = "callForwardingNoReply";
+ break;
+ case 6:
+ r = "callDeflection";
+ break;
+ case 7:
+ r = "callTransfer";
+ break;
+ default:
+ sprintf(buffer, "unknown billing-id value %d!", val);
+ r = buffer;
+ }
+ return(r);
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component
+ *---------------------------------------------------------------------------*/
+static void
+F_1_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t invokeComponent\n");
+ state = ST_EXP_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result
+ *---------------------------------------------------------------------------*/
+static void
+F_1_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnResult\n");
+ state = ST_EXP_RR_INV_ID;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * return error
+ *---------------------------------------------------------------------------*/
+static void
+F_1_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_3, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t returnError\n");
+ state = ST_EXP_NIX;
+ }
+}
+/*---------------------------------------------------------------------------*
+ * reject
+ *---------------------------------------------------------------------------*/
+static void
+F_1_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_1_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t reject\n");
+ state = ST_EXP_REJ_INV_ID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_REJ_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, general problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ30(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ30, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t General problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized component\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped component\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = badly structured component\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, invoke problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ31(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ31, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Invoke problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = duplicate invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized operation\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped argument\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = resource limitation\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = initiator releasing\n");
+ break;
+ case 5:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized linked identifier\n");
+ break;
+ case 6:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = linked resonse unexpected\n");
+ break;
+ case 7:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected child operation\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return result problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ32(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ32, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return result problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = return response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped result\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * reject, return error problem
+ *---------------------------------------------------------------------------*/
+static void
+F_RJ33(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RJ33, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Return error problem\n");
+ }
+ else
+ {
+ switch(val)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized invocation\n");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = error response unexpected\n");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unrecognized error\n");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unexpected error\n");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = mistyped parameter\n");
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), "\t problem = unknown problem code 0x%x\n", val);
+ break;
+ }
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: invoke id
+ *---------------------------------------------------------------------------*/
+static void
+F_RR2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR2, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t InvokeIdentifier = %d\n", val);
+ state = ST_EXP_RR_OP_VAL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * invoke component: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_INFO;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: operation value
+ *---------------------------------------------------------------------------*/
+static void
+F_RR3(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RR3, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t Operation Value = %s (%d)\n", opval_str(val), val);
+ state = ST_EXP_RR_RESULT;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * return result: RESULT
+ *---------------------------------------------------------------------------*/
+static void
+F_RRR(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_RRR, val = %d\n", val);
+#endif
+ state = ST_EXP_NIX;
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t specificChargingUnits\n");
+ state = ST_EXP_RUL;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_1(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_1, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t freeOfCharge\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_4_2(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_4_2, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t chargeNotAvailable\n");
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_5(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_5, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t recordedUnitsList [1]\n");
+ state = ST_EXP_RU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_6(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_6, val = %d\n", val);
+#endif
+ if(val == -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t RecordedUnits\n");
+ state = ST_EXP_RNOU;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_7(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_7, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t NumberOfUnits = %d\n", val);
+ state = ST_EXP_TOCI;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_8(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_8, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t typeOfChargingInfo = %s\n", val == 0 ? "subTotal" : "total");
+ state = ST_EXP_DBID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+static void
+F_9(char *pbuf, int val)
+{
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: exec F_9, val = %d\n", val);
+#endif
+ if(val != -1)
+ {
+ sprintf((pbuf+strlen(pbuf)), "\t AOCDBillingId = %s (%d)\n", bid_str(val), val);
+ state = ST_EXP_NIX;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * state table
+ *---------------------------------------------------------------------------*/
+static struct statetab {
+ int currstate; /* input: current state we are in */
+ int form; /* input: current tag form */
+ int class; /* input: current tag class */
+ int code; /* input: current tag code */
+ void (*func)(char *,int); /* output: func to exec */
+} statetab[] = {
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+
+/* invoke */
+
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_1_1 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 2, F_1_2 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 3, F_1_3 },
+ {ST_EXP_COMP_TYP, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 4, F_1_4 },
+ {ST_EXP_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_2 },
+ {ST_EXP_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_3 },
+ {ST_EXP_INFO, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_4 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_NULL, F_4_1 },
+ {ST_EXP_INFO, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_4_2 },
+ {ST_EXP_RUL, FAC_TAGFORM_CON, FAC_TAGCLASS_COS, 1, F_5 },
+ {ST_EXP_RU, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SEQ, F_6 },
+ {ST_EXP_RNOU, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_7 },
+ {ST_EXP_TOCI, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_8 },
+ {ST_EXP_DBID, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_9 },
+
+/* return result */
+
+ {ST_EXP_RR_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR2 },
+ {ST_EXP_RR_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RR3 },
+ {ST_EXP_RR_RESULT, FAC_TAGFORM_CON, FAC_TAGCLASS_UNI, FAC_CODEUNI_SET, F_RRR },
+
+/* current state tag form tag class tag code function */
+/* --------------------- ---------------------- ---------------------- ---------------------- ----------------*/
+/* reject */
+
+ {ST_EXP_REJ_INV_ID, FAC_TAGFORM_PRI, FAC_TAGCLASS_UNI, FAC_CODEUNI_INT, F_RJ2 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 0, F_RJ30 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 1, F_RJ31 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 2, F_RJ32 },
+ {ST_EXP_REJ_OP_VAL, FAC_TAGFORM_PRI, FAC_TAGCLASS_COS, 3, F_RJ33 },
+
+/* end */
+
+ {-1, -1, -1, -1, NULL }
+};
+
+/*---------------------------------------------------------------------------*
+ * state decode for do_component
+ *---------------------------------------------------------------------------*/
+static void
+next_state(char *pbuf, int class, int form, int code, int val)
+{
+ int i;
+
+#ifdef ST_DEBUG
+ sprintf((pbuf+strlen(pbuf)), "next_state: class=%d, form=%d, code=%d, val=%d\n", class, form, code, val);
+#endif
+
+ for(i=0; ; i++)
+ {
+ if((statetab[i].currstate > state) ||
+ (statetab[i].currstate == -1))
+ {
+ break;
+ }
+
+ if((statetab[i].currstate == state) &&
+ (statetab[i].form == form) &&
+ (statetab[i].class == class) &&
+ (statetab[i].code == code))
+ {
+ (*statetab[i].func)(pbuf, val);
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * decode OBJECT IDENTIFIER
+ *---------------------------------------------------------------------------*/
+static void
+object_id(int comp_length, unsigned char *pbuf)
+{
+ int x;
+ int i;
+ int j = 0;
+ int id_org = 0;
+ int etsi = 0;
+
+ sprintf((pbuf+strlen(pbuf)), "\t");
+
+ for(i = comp_length-1; i >= 0; i--, j++)
+ {
+ sprintf((pbuf+strlen(pbuf)), "0x%02x = %d", *byte_buf, *byte_buf);
+
+ if(j == 0)
+ {
+ x = *byte_buf;
+
+ if(x >= 0 && x <= 39)
+ {
+ sprintf((pbuf+strlen(pbuf)), " ccitt/itu-t (0)");
+ switch(x)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " recommendation (0)");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " question (1)");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " administration (2)");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " network-operator (3)");
+ break;
+ case 4:
+ sprintf((pbuf+strlen(pbuf)), " identified-organization (4)");
+ id_org = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " error: undefined-identifier (%d)", x);
+ break;
+ }
+ }
+ else if(x >= 40 && x <= 79)
+ {
+ sprintf((pbuf+strlen(pbuf)), " iso (1)");
+ x -= 40;
+ switch(x)
+ {
+ case 0:
+ sprintf((pbuf+strlen(pbuf)), " standard (0)");
+ break;
+ case 1:
+ sprintf((pbuf+strlen(pbuf)), " registration-authority (1)");
+ break;
+ case 2:
+ sprintf((pbuf+strlen(pbuf)), " member-body (2)");
+ break;
+ case 3:
+ sprintf((pbuf+strlen(pbuf)), " identified-organization (3)");
+ id_org = 1;
+ break;
+ default:
+ sprintf((pbuf+strlen(pbuf)), " error: undefined-identifier (%d)", x);
+ break;
+ }
+ }
+ else
+ {
+ x -= 80;
+ sprintf((pbuf+strlen(pbuf)), " joint-iso-ccitt (3) ??? (%d)", x);
+ }
+ }
+
+ if(j == 1)
+ {
+ if(id_org == 1)
+ {
+ if(*byte_buf == 0)
+ {
+ sprintf((pbuf+strlen(pbuf)), " etsi (0)");
+ etsi = 1;
+ }
+ }
+ }
+
+ if(j == 2)
+ {
+ if(etsi == 1)
+ {
+ if(*byte_buf == 0)
+ {
+ sprintf((pbuf+strlen(pbuf)), " mobileDomain (0)");
+ }
+ if(*byte_buf == 1)
+ {
+ sprintf((pbuf+strlen(pbuf)), " inDomain (1)");
+ }
+ }
+ }
+
+ byte_buf++;
+ byte_len++;
+
+ if(i)
+ sprintf((pbuf+strlen(pbuf)), "\n\t");
+ else
+ sprintf((pbuf+strlen(pbuf)), "\n");
+ }
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/q932_fac.h b/usr.sbin/i4b/isdntrace/q932_fac.h
new file mode 100644
index 0000000..563de3a
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q932_fac.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q932_fac.h - facility header file
+ * ---------------------------------
+ *
+ * $Id: q932_fac.h,v 1.7 2000/02/18 16:27:39 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Fri Feb 18 17:26:07 2000]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - Q.932 (03/93) Generic Procedures for the Control of
+ * ISDN Supplementaty Services
+ * - Q.950 (03/93) Supplementary Services Protocols, Structure and
+ * General Principles
+ * - ETS 300 179 (10/92) Advice Of Charge: charging information during
+ * the call (AOC-D) supplementary service Service description
+ * - ETS 300 180 (10/92) Advice Of Charge: charging information at the
+ * end of call (AOC-E) supplementary service Service description
+ * - ETS 300 181 (04/93) Advice Of Charge (AOC) supplementary service
+ * Functional capabilities and information flows
+ * - ETS 300 182 (04/93) Advice Of Charge (AOC) supplementary service
+ * Digital Subscriber Signalling System No. one (DSS1) protocol
+ * - X.208 Specification of Abstract Syntax Notation One (ASN.1)
+ * - X.209 Specification of Basic Encoding Rules for
+ * Abstract Syntax Notation One (ASN.1)
+ * - "ASN.1 Abstract Syntax Notation One", Walter Gora, DATACOM-Verlag
+ * 1992, 3rd Edition (ISBN 3-89238-062-7) (german !)
+ *
+ *---------------------------------------------------------------------------*/
+
+/* #define FAC_DEBUG */
+/* #define ST_DEBUG */
+
+/* protocols */
+#define FAC_PROTO_ROP 0x11
+#define FAC_PROTO_CMIP 0x12
+#define FAC_PROTO_ACSE 0x13
+
+/* tag classes */
+#define FAC_TAGCLASS_UNI 0x00
+#define FAC_TAGCLASS_APW 0x01
+#define FAC_TAGCLASS_COS 0x02
+#define FAC_TAGCLASS_PRU 0x03
+
+/* tag forms */
+#define FAC_TAGFORM_PRI 0x00
+#define FAC_TAGFORM_CON 0x01
+
+/* class UNIVERSAL values */
+#define FAC_CODEUNI_BOOL 1
+#define FAC_CODEUNI_INT 2
+#define FAC_CODEUNI_BITS 3
+#define FAC_CODEUNI_OCTS 4
+#define FAC_CODEUNI_NULL 5
+#define FAC_CODEUNI_OBJI 6
+#define FAC_CODEUNI_OBJD 7
+#define FAC_CODEUNI_EXT 8
+#define FAC_CODEUNI_REAL 9
+#define FAC_CODEUNI_ENUM 10
+#define FAC_CODEUNI_R11 11
+#define FAC_CODEUNI_R12 12
+#define FAC_CODEUNI_R13 13
+#define FAC_CODEUNI_R14 14
+#define FAC_CODEUNI_R15 15
+#define FAC_CODEUNI_SEQ 16
+#define FAC_CODEUNI_SET 17
+#define FAC_CODEUNI_NSTR 18
+#define FAC_CODEUNI_PSTR 19
+#define FAC_CODEUNI_TSTR 20
+#define FAC_CODEUNI_VSTR 21
+#define FAC_CODEUNI_ISTR 22
+#define FAC_CODEUNI_UTIME 23
+#define FAC_CODEUNI_GTIME 24
+#define FAC_CODEUNI_GSTR 25
+#define FAC_CODEUNI_VISTR 26
+#define FAC_CODEUNI_GNSTR 27
+
+/* operation values */
+#define FAC_OPVAL_UUS 1
+#define FAC_OPVAL_CUG 2
+#define FAC_OPVAL_MCID 3
+#define FAC_OPVAL_BTPY 4
+#define FAC_OPVAL_ETPY 5
+#define FAC_OPVAL_ECT 6
+
+#define FAC_OPVAL_DIV_ACT 7
+#define FAC_OPVAL_DIV_DEACT 8
+#define FAC_OPVAL_DIV_ACTSN 9
+#define FAC_OPVAL_DIV_DEACTSN 10
+#define FAC_OPVAL_DIV_INTER 11
+#define FAC_OPVAL_DIV_INFO 12
+#define FAC_OPVAL_DIV_CALLDEF 13
+#define FAC_OPVAL_DIV_CALLRER 14
+#define FAC_OPVAL_DIV_LINF2 15
+#define FAC_OPVAL_DIV_INVS 16
+#define FAC_OPVAL_DIV_INTER1 17
+#define FAC_OPVAL_DIV_LINF1 18
+#define FAC_OPVAL_DIV_LINF3 19
+
+#define FAC_OPVAL_ER_CRCO 20
+#define FAC_OPVAL_ER_MGMT 21
+#define FAC_OPVAL_ER_CANC 22
+
+#define FAC_OPVAL_MLPP_QUERY 24
+#define FAC_OPVAL_MLPP_CALLR 25
+#define FAC_OPVAL_MLPP_CALLP 26
+
+#define FAC_OPVAL_AOC_REQ 30
+#define FAC_OPVAL_AOC_S_CUR 31
+#define FAC_OPVAL_AOC_S_SPC 32
+#define FAC_OPVAL_AOC_D_CUR 33
+#define FAC_OPVAL_AOC_D_UNIT 34
+#define FAC_OPVAL_AOC_E_CUR 35
+#define FAC_OPVAL_AOC_E_UNIT 36
+#define FAC_OPVAL_AOC_IDOFCRG 37
+
+#define FAC_OPVAL_CONF_BEG 40
+#define FAC_OPVAL_CONF_ADD 41
+#define FAC_OPVAL_CONF_SPLIT 42
+#define FAC_OPVAL_CONF_DROP 43
+#define FAC_OPVAL_CONF_ISOLATE 44
+#define FAC_OPVAL_CONF_REATT 45
+#define FAC_OPVAL_CONF_PDISC 46
+#define FAC_OPVAL_CONF_FCONF 47
+#define FAC_OPVAL_CONF_END 48
+#define FAC_OPVAL_CONF_IDCFE 49
+
+#define FAC_OPVAL_REVC_REQ 60
+
+enum states {
+ ST_EXP_COMP_TYP,
+ ST_EXP_INV_ID,
+ ST_EXP_OP_VAL,
+ ST_EXP_INFO,
+ ST_EXP_RUL,
+ ST_EXP_RU,
+ ST_EXP_RNOU,
+ ST_EXP_TOCI,
+ ST_EXP_DBID,
+
+ ST_EXP_RR_INV_ID,
+ ST_EXP_RR_OP_VAL,
+ ST_EXP_RR_RESULT,
+
+ ST_EXP_REJ_INV_ID,
+ ST_EXP_REJ_OP_VAL,
+ ST_EXP_REJ_RESULT,
+
+ ST_EXP_NIX
+};
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/trace.c b/usr.sbin/i4b/isdntrace/trace.c
new file mode 100644
index 0000000..2ba1ca5
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/trace.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright (c) 1996, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Copyright (c) 1996 Gary Jennejohn. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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.c - print traces of D (B) channel activity for isdn4bsd
+ * -------------------------------------------------------------
+ *
+ * $Id: trace.c,v 1.19 2000/08/28 07:06:42 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Aug 28 09:03:46 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+#include <unistd.h>
+
+unsigned char buf[BSIZE];
+FILE *Fout = NULL;
+FILE *BP = NULL;
+int outflag = 1;
+int header = 1;
+int print_q921 = 1;
+int unit = 0;
+int dchan = 0;
+int bchan = 0;
+int traceon = 0;
+int analyze = 0;
+int Rx = RxUDEF;
+int Tx = TxUDEF;
+int f;
+int Bopt = 0;
+int Popt = 0;
+int bpopt = 0;
+int info = 0;
+int Fopt = 0;
+int xopt = 1;
+
+int enable_trace = TRACE_D_RX | TRACE_D_TX;
+
+static char outfilename[MAXPATHLEN];
+static char routfilename[MAXPATHLEN];
+static char BPfilename[MAXPATHLEN];
+static char rBPfilename[MAXPATHLEN];
+
+static struct stat fst;
+
+static void dumpbuf( int n, unsigned char *buf, i4b_trace_hdr_t *hdr, int raw );
+static int switch_driver( int value, int rx, int tx );
+static void usage( void );
+static void exit_hdl( void );
+static void reopenfiles( int );
+void add_datetime(char *filename, char *rfilename);
+
+/*---------------------------------------------------------------------------*
+ * usage instructions
+ *---------------------------------------------------------------------------*/
+void
+usage(void)
+{
+ fprintf(stderr,"\n");
+ fprintf(stderr,"isdntrace - i4b package ISDN trace facility for passive cards (%02d.%02d.%d)\n", VERSION, REL, STEP);
+ fprintf(stderr,"usage: isdntrace -a -b -d -f <file> -h -i -l -n <val> -o -p <file> -r -u <unit>\n");
+ fprintf(stderr," -x -B -F -P -R <unit> -T <unit>\n");
+ fprintf(stderr," -a analyzer mode ................................... (default off)\n");
+ fprintf(stderr," -b switch B channel trace on ....................... (default off)\n");
+ fprintf(stderr," -d switch D channel trace off ....................... (default on)\n");
+ fprintf(stderr," -f <file> write output to file filename ............ (default %s0)\n", TRACE_FILE_NAME);
+ fprintf(stderr," -h don't print header for each message ............. (default off)\n");
+ fprintf(stderr," -i print I.430 (layer 1) INFO signals .............. (default off)\n");
+ fprintf(stderr," -l don't decode low layer Q.921 messages ........... (default off)\n");
+ fprintf(stderr," -n <val> process packet if it is longer than <val> octetts . (default 0)\n");
+ fprintf(stderr," -o don't write output to a file .................... (default off)\n");
+ fprintf(stderr," -p <file> specify filename for -B and -P ........ (default %s0)\n", BIN_FILE_NAME);
+ fprintf(stderr," -r don't print raw hex/ASCII dump of protocol ...... (default off)\n");
+ fprintf(stderr," -u <unit> specify controller unit number ............... (default unit 0)\n");
+ fprintf(stderr," -x show packets with unknown protocol discriminator (default off)\n");
+ fprintf(stderr," -B write binary trace data to file filename ........ (default off)\n");
+ fprintf(stderr," -F with -P and -p: wait for more data at EOF ....... (default off)\n");
+ fprintf(stderr," -P playback from binary trace data file ............ (default off)\n");
+ fprintf(stderr," -R <unit> analyze Rx controller unit number (for -a) ... (default unit %d)\n", RxUDEF);
+ fprintf(stderr," -T <unit> analyze Tx controller unit number (for -a) ... (default unit %d)\n", TxUDEF);
+ fprintf(stderr,"\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * main
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char *argv[])
+{
+ char devicename[80];
+ char headerbuf[256];
+
+ int n;
+ int c;
+ char *b;
+
+ char *outfile = TRACE_FILE_NAME;
+ char *binfile = BIN_FILE_NAME;
+ int outfileset = 0;
+ int raw = 1;
+ int noct = -1;
+ time_t tm;
+ i4b_trace_hdr_t *ithp = NULL;
+ int l;
+ static struct stat fstnew;
+
+ b = &buf[sizeof(i4b_trace_hdr_t)];
+
+ while( (c = getopt(argc, argv, "abdf:hiln:op:ru:xBFPR:T:")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ analyze = 1;
+ break;
+
+ case 'b':
+ enable_trace |= (TRACE_B_RX | TRACE_B_TX);
+ break;
+
+ case 'd':
+ enable_trace &= (~(TRACE_D_TX | TRACE_D_RX));
+ break;
+
+ case 'o':
+ outflag = 0;
+ break;
+
+ case 'f':
+ outfile = optarg;
+ outfileset = 1;
+ break;
+
+ case 'n':
+ noct = atoi(optarg);
+ break;
+
+ case 'h':
+ header = 0;
+ break;
+
+ case 'i':
+ enable_trace |= TRACE_I;
+ info = 1;
+ break;
+
+ case 'l':
+ print_q921 = 0;
+ break;
+
+ case 'p':
+ binfile = optarg;
+ bpopt = 1;
+ break;
+
+ case 'r':
+ raw = 0;
+ break;
+
+ case 'u':
+ unit = atoi(optarg);
+ if(unit < 0 || unit >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'x':
+ xopt = 0;
+ break;
+
+ case 'B':
+ Bopt = 1;
+ break;
+
+ case 'F':
+ Fopt = 1;
+ break;
+
+ case 'P':
+ Popt = 1;
+ break;
+
+ case 'R':
+ Rx = atoi(optarg);
+ if(Rx < 0 || Rx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case 'T':
+ Tx = atoi(optarg);
+ if(Tx < 0 || Tx >= MAX_CONTROLLERS)
+ usage();
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(enable_trace == 0)
+ usage();
+
+ if(Bopt && Popt)
+ usage();
+
+ atexit(exit_hdl);
+
+ if(Bopt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ add_datetime(BPfilename, rBPfilename);
+
+ if((BP = fopen(rBPfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Popt)
+ {
+ if(bpopt)
+ sprintf(BPfilename, "%s", binfile);
+ else
+ sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
+
+ strcpy(rBPfilename, BPfilename);
+
+ if((BP = fopen(BPfilename, "r")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ if(Fopt)
+ {
+ if(fstat(fileno(BP), &fst))
+ {
+ char buffer[80];
+ sprintf(buffer, "Error fstat file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+ }
+ else
+ {
+ sprintf(devicename, "%s%d", I4BTRC_DEVICE, unit);
+
+ if((f = open(devicename, O_RDWR)) < 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening trace device [%s]", devicename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(outflag)
+ {
+ if(outfileset == 0)
+ sprintf(outfilename, "%s%d", TRACE_FILE_NAME, unit);
+ else
+ strcpy(outfilename, outfile);
+
+ add_datetime(outfilename, routfilename);
+
+ if((Fout = fopen(routfilename, "w")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error opening file [%s]", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting file [%s] to unbuffered", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if((setvbuf(stdout, (char *)NULL, _IOLBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error setting stdout to line-buffered");
+ perror(buffer);
+ exit(1);
+ }
+
+ if(!Popt)
+ {
+ if((switch_driver(enable_trace, Rx, Tx)) == -1)
+ exit(1);
+ else
+ traceon = 1;
+ }
+
+ signal(SIGHUP, SIG_IGN); /* ignore hangup signal */
+ signal(SIGUSR1, reopenfiles); /* rotate logfile(s) */
+
+ time(&tm);
+
+ if(analyze)
+ {
+ sprintf(headerbuf, "\n==== isdnanalyze controller rx #%d - tx #%d ==== started %s",
+ Rx, Tx, ctime(&tm));
+ }
+ else
+ {
+ sprintf(headerbuf, "\n=========== isdntrace controller #%d =========== started %s",
+ unit, ctime(&tm));
+ }
+
+ printf("%s", headerbuf);
+
+ if(outflag)
+ fprintf(Fout, "%s", headerbuf);
+
+ for (;;)
+ {
+ if(Popt == 0)
+ {
+ n = read(f, buf, BSIZE);
+
+ if(Bopt)
+ {
+ if((fwrite(buf, 1, n, BP)) != n)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error writing file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ n -= sizeof(i4b_trace_hdr_t);
+ }
+ else
+ {
+again:
+ if((fread(buf, 1, sizeof(i4b_trace_hdr_t), BP)) != sizeof(i4b_trace_hdr_t))
+ {
+ if(feof(BP))
+ {
+ if(Fopt)
+ {
+ if(ferror(BP))
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading hdr from file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ usleep(250000);
+ clearerr(BP);
+
+ if(stat(rBPfilename, &fstnew) != -1)
+ {
+ if((fst.st_ino != fstnew.st_ino) ||
+ (fstnew.st_nlink == 0))
+ {
+ if((BP = freopen(rBPfilename, "r", BP)) == NULL)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reopening file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ stat(rBPfilename, &fst);
+ }
+ }
+ goto again;
+ }
+ else
+ {
+ printf("\nEnd of playback input file reached.\n");
+ exit(0);
+ }
+ }
+ else
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading hdr from file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ ithp = (i4b_trace_hdr_t *)buf;
+ l = ithp->length - sizeof(i4b_trace_hdr_t);
+
+ if((n = fread(buf+sizeof(i4b_trace_hdr_t), 1, l , BP)) != l)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reading data from file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ }
+
+ if((n > 0) && (n > noct))
+ {
+ dumpbuf(n, b, (i4b_trace_hdr_t *)buf, raw);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * format header into static buffer, return buffer address
+ *---------------------------------------------------------------------------*/
+char *
+fmt_hdr(i4b_trace_hdr_t *hdr, int frm_len)
+{
+ struct tm *s;
+ static char hbuf[256];
+ int i = 0;
+
+ s = localtime((time_t *)&(hdr->time.tv_sec));
+
+ if(hdr->type == TRC_CH_I) /* Layer 1 INFO's */
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d ---------------- time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec);
+ }
+ else
+ {
+ if(hdr->trunc > 0)
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d (%d) ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len,
+ hdr->trunc);
+ }
+ else
+ {
+ sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d ",
+ ((hdr->dir) ? "NT->TE" : "TE->NT"),
+ hdr->unit,
+ hdr->count,
+ s->tm_mday,
+ s->tm_mon + 1,
+ s->tm_hour,
+ s->tm_min,
+ s->tm_sec,
+ (u_int32_t)hdr->time.tv_usec,
+ frm_len);
+ }
+ }
+
+ for(i=strlen(hbuf); i <= NCOLS;)
+ hbuf[i++] = '-';
+
+ hbuf[i++] = '\n';
+ hbuf[i] = '\0';
+
+ return(hbuf);
+}
+
+/*---------------------------------------------------------------------------*
+ * decode protocol and output to file(s)
+ *---------------------------------------------------------------------------*/
+static void
+dumpbuf(int n, unsigned char *buf, i4b_trace_hdr_t *hdr, int raw)
+{
+ static char l1buf[128];
+ static unsigned char l2buf[32000];
+ static unsigned char l3buf[32000];
+ int cnt;
+ int nsave = n;
+ char *pbuf;
+ int i, j;
+
+ l1buf[0] = '\0';
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+
+ switch(hdr->type)
+ {
+ case TRC_CH_I: /* Layer 1 INFO's */
+
+ /* on playback, don't display layer 1 if -i ! */
+ if(!(enable_trace & TRACE_I))
+ break;
+
+ pbuf = &l1buf[0];
+
+ switch(buf[0])
+ {
+ case INFO0:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO0 (No Signal)\n");
+ break;
+
+ case INFO1_8:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO1 (Activation Request, Priority = 8, from TE)\n");
+ break;
+
+ case INFO1_10:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO1 (Activation Request, Priority = 10, from TE)\n");
+ break;
+
+ case INFO2:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO2 (Pending Activation, from NT)\n");
+ break;
+
+ case INFO3:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO3 (Synchronized, from TE)\n");
+ break;
+
+ case INFO4_8:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO4 (Activated, Priority = 8/9, from NT)\n");
+ break;
+
+ case INFO4_10:
+ sprintf((pbuf+strlen(pbuf)),"I430: INFO4 (Activated, Priority = 10/11, from NT)\n");
+ break;
+
+ default:
+ sprintf((pbuf+strlen(pbuf)),"I430: ERROR, invalid INFO value 0x%x!\n", buf[0]);
+ break;
+ }
+ break;
+
+ case TRC_CH_D: /* D-channel data */
+
+ cnt = decode_lapd(l2buf, n, buf, hdr->dir, raw, print_q921);
+
+ n -= cnt;
+ buf += cnt;
+
+ if(n)
+ {
+ switch(*buf)
+ {
+ case 0x40:
+ case 0x41:
+ decode_1tr6(l3buf, n, cnt, buf, raw);
+ break;
+
+ case 0x08:
+ decode_q931(l3buf, n, cnt, buf, raw);
+ break;
+
+ default:
+ if(xopt)
+ {
+ l2buf[0] = '\0';
+ l3buf[0] = '\0';
+ }
+ else
+ {
+ decode_unknownl3(l3buf, n, cnt, buf, raw);
+ }
+ break;
+ }
+ }
+ break;
+
+ default: /* B-channel data */
+
+ pbuf = &l2buf[0];
+
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"B%d:%.3x ", hdr->type, i);
+
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ sprintf((pbuf+strlen(pbuf))," ");
+
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ break;
+ }
+
+ if(header && ((l1buf[0] != '\0' || l2buf[0] != '\0') || (l3buf[0] != 0)))
+ {
+ char *p;
+ p = fmt_hdr(hdr, nsave);
+ printf("%s", p);
+ if(outflag)
+ fprintf(Fout, "%s", p);
+ }
+
+ if(l1buf[0] != '\0')
+ {
+ printf("%s", l1buf);
+ if(outflag)
+ fprintf(Fout, "%s", l1buf);
+ }
+
+ if(l2buf[0] != '\0')
+ {
+ printf("%s", l2buf);
+ if(outflag)
+ fprintf(Fout, "%s", l2buf);
+ }
+
+ if(l3buf[0] != '\0')
+ {
+ printf("%s", l3buf);
+ if(outflag)
+ fprintf(Fout, "%s", l3buf);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * exit handler function to be called at program exit
+ *---------------------------------------------------------------------------*/
+void
+exit_hdl()
+{
+ if(traceon)
+ switch_driver(TRACE_OFF, Rx, Tx);
+}
+
+/*---------------------------------------------------------------------------*
+ * switch driver debugging output on/off
+ *---------------------------------------------------------------------------*/
+static int
+switch_driver(int value, int rx, int tx)
+{
+ char buffer[80];
+ int v = value;
+
+ if(analyze == 0)
+ {
+ if(ioctl(f, I4B_TRC_SET, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SET, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ if(value == TRACE_OFF)
+ {
+ if(ioctl(f, I4B_TRC_RESETA, &v) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_RESETA - ");
+ perror(buffer);
+ return(-1);
+ }
+ }
+ else
+ {
+ i4b_trace_setupa_t tsa;
+
+ tsa.rxunit = rx;
+ tsa.rxflags = value;
+ tsa.txunit = tx;
+ tsa.txflags = value;
+
+ if(ioctl(f, I4B_TRC_SETA, &tsa) < 0)
+ {
+ sprintf(buffer, "Error ioctl I4B_TRC_SETA, val = %d", v);
+ perror(buffer);
+ return(-1);
+ }
+ }
+ }
+ return(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * reopen files to support rotating logfile(s) on SIGUSR1
+ *
+ * based on an idea from Ripley (ripley@nostromo.in-berlin.de)
+ *
+ * close file and reopen it for append. this will be a nop
+ * if the previously opened file hasn't moved but will open
+ * a new one otherwise, thus enabling a rotation...
+ *
+ *---------------------------------------------------------------------------*/
+static void
+reopenfiles(int dummy)
+{
+ if(outflag)
+ {
+ fclose(Fout);
+
+ add_datetime(outfilename, routfilename);
+
+ if((Fout = fopen(routfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", routfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(Bopt)
+ {
+
+ fclose(BP);
+
+ add_datetime(BPfilename, rBPfilename);
+
+ if((BP = fopen(rBPfilename, "a")) == NULL)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-opening file [%s]", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
+ {
+ char buffer[80];
+
+ sprintf(buffer, "Error re-setting file [%s] to unbuffered", rBPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+}
+
+void
+add_datetime(char *filename, char *rfilename)
+{
+ time_t timeb;
+ struct tm *tmp;
+ FILE *fx;
+
+ time(&timeb);
+ tmp = localtime(&timeb);
+
+ sprintf(rfilename, "%s-", filename);
+
+ strftime(rfilename+strlen(rfilename), MAXPATHLEN-strlen(rfilename)-1,
+ "%Y%m%d-%H%M%S", tmp);
+
+ if((fx = fopen(rfilename, "r")) != NULL)
+ {
+ fclose(fx);
+
+ sleep(1);
+
+ time(&timeb);
+ tmp = localtime(&timeb);
+
+ sprintf(rfilename, "%s-", filename);
+
+ strftime(rfilename+strlen(rfilename), MAXPATHLEN-strlen(rfilename)-1,
+ "%Y%m%d-%H%M%S", tmp);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/trace.h b/usr.sbin/i4b/isdntrace/trace.h
new file mode 100644
index 0000000..81fa415
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/trace.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1996, 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Copyright (c) 1996 Gary Jennejohn. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * 4. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software and/or documentation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 - header file for isdn trace
+ * ------------------------------------
+ *
+ * $Id: trace.h,v 1.12 2000/02/14 16:25:22 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Mon Feb 14 14:43:40 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_trace.h>
+
+#include "pcause_1tr6.h" /* obsolete german national ISDN */
+#include "pcause_q850.h"
+
+#define I4BTRC_DEVICE "/dev/i4btrc" /* trace device file */
+#define TRACE_FILE_NAME "isdntrace" /* default output filename */
+#define BIN_FILE_NAME "isdntracebin" /* default binary filename */
+
+#define BSIZE 4096 /* read buffer size */
+#define NCOLS 80 /* screen width */
+
+#define RxUDEF 0 /* analyze mode, default unit for receiver side */
+#define TxUDEF 1 /* analyze mode, default unit for transmitter side */
+
+int decode_lapd(char *pbuf, int n, unsigned char *buf, int is_te, int raw, int printit);
+void decode_q931(char *pbuf, int n, int off, unsigned char *buf, int raw);
+void decode_unknownl3(char *pbuf, int n, int off, unsigned char *buf, int raw);
+void decode_1tr6(char *pbuf, int n, int off, unsigned char *buf, int raw);
+char *print_error(int prot, unsigned char code);
+int q931_facility(char *pbuf, unsigned char *buf);
+int p_q931cause(char *pbuf, unsigned char *buf);
+int p_q931address(char *pbuf, unsigned char *buf);
+int p_q931bc(char *pbuf, unsigned char *buf);
+int p_q931high_compat(char *pbuf, unsigned char *buf);
+int q932_facility(char *pbuf, unsigned char *buf);
+int p_q931user_user(char *pbuf, unsigned char *buf);
+int p_q931notification(char *pbuf, unsigned char *buf);
+int p_q931redir(char *pbuf, unsigned char *buf);
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/unknownl3.c b/usr.sbin/i4b/isdntrace/unknownl3.c
new file mode 100644
index 0000000..8967a6a9
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/unknownl3.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2000 Hellmuth Michaelis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * unknownl3.c - print L3 packets with unknown PD
+ * ----------------------------------------------
+ *
+ * $Id: unknownl3.c,v 1.2 2000/02/13 15:26:52 hm Exp $
+ *
+ * $FreeBSD$
+ *
+ * last edit-date: [Sun Feb 13 14:16:44 2000]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.h"
+
+/*---------------------------------------------------------------------------*
+ * decode unknown protocol
+ *---------------------------------------------------------------------------*/
+void
+decode_unknownl3(char *pbuf, int n, int off, unsigned char *buf, int raw)
+{
+ int pd;
+ int j;
+ int i;
+
+ if(n <= 0)
+ return;
+
+ *pbuf = '\0';
+
+ if(raw)
+ {
+ for (i = 0; i < n; i += 16)
+ {
+ sprintf((pbuf+strlen(pbuf)),"Dump:%.3d ", i+off);
+ for (j = 0; j < 16; j++)
+ if (i + j < n)
+ sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf))," ");
+ sprintf((pbuf+strlen(pbuf))," ");
+ for (j = 0; j < 16 && i + j < n; j++)
+ if (isprint(buf[i + j]))
+ sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
+ else
+ sprintf((pbuf+strlen(pbuf)),".");
+ sprintf((pbuf+strlen(pbuf)),"\n");
+ }
+ }
+
+ i = 0;
+
+ /* protocol discriminator */
+
+ pd = buf[i];
+
+ sprintf((pbuf+strlen(pbuf)), "PD%02X: ", pd);
+
+ if(pd >= 0x00 && pd <= 0x07)
+ sprintf((pbuf+strlen(pbuf)), "pd=User-User (0x%02x)",pd);
+ else if(pd == 0x08)
+ sprintf((pbuf+strlen(pbuf)), "pd=Q.931/I.451");
+ else if(pd >= 0x10 && pd <= 0x3f)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)",pd);
+ else if(pd >= 0x40 && pd <= 0x4f)
+ sprintf((pbuf+strlen(pbuf)), "pd=National Use (0x%02x)",pd);
+ else if(pd >= 0x50 && pd <= 0xfe)
+ sprintf((pbuf+strlen(pbuf)), "pd=Other Layer 3 or X.25 (0x%02x)",pd);
+ else
+ sprintf((pbuf+strlen(pbuf)), "pd=Reserved (0x%02x)",pd);
+
+ sprintf((pbuf+strlen(pbuf)), "\n [");
+ for(j = 0; j < (n-i); j++)
+ {
+ sprintf((pbuf+strlen(pbuf)),"0x%02x ", buf[j+i]);
+ }
+
+ sprintf((pbuf+strlen(pbuf)),"]\n");
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/man/Makefile b/usr.sbin/i4b/man/Makefile
new file mode 100644
index 0000000..561e5fe
--- /dev/null
+++ b/usr.sbin/i4b/man/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+MAN= i4b.4 i4bcapi.4 i4bctl.4 i4bing.4 i4bipr.4 i4bisppp.4 i4bq921.4 \
+ i4bq931.4 i4brbch.4 i4btel.4 i4btrc.4 iavc.4 isic.4 ifpi.4 \
+ ifpi2.4 ifpnp.4 ihfc.4 itjc.4 iwic.4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/man/i4b.4 b/usr.sbin/i4b/man/i4b.4
new file mode 100644
index 0000000..f2e031a
--- /dev/null
+++ b/usr.sbin/i4b/man/i4b.4
@@ -0,0 +1,110 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 12:07:13 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4B 4
+.Os
+.Sh NAME
+.Nm i4b
+.Nd isdn4bsd call control ISDN driver
+.Sh SYNOPSIS
+.Cd device \&"i4b\&"
+.Sh DESCRIPTION
+The
+.Nm
+device driver is used by the
+.Xr isdnd 8
+daemon to exchange messages with the isdn4bsd kernel part for the purpose
+of call establishment, control and disconnection and to access various
+control and status informations.
+The
+.Nm
+device can only be opened by a single process and is not meant to be used
+by user programs.
+.Pp
+The messages and message parameters are documented in the include
+file
+.Em /usr/include/machine/i4b_ioctl.h .
+.Pp
+The most important available ioctl's are:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I4B_CDID_REQ
+Request a unique Call Description IDentifier (cdid) which identifies
+uniquely a single interaction of the local D channel with the exchange.
+.It Ar I4B_CONNECT_REQ
+Actively request a call setup to a remote ISDN subscriber.
+.It Ar I4B_CONNECT_RESP
+Respond to an incoming call, either accept, reject or ignore it.
+.It Ar I4B_DISCONNECT_REQ
+Actively terminate a connection.
+.It Ar I4B_CTRL_INFO_REQ
+Request information about an installed ISDN controller card.
+.It Ar I4B_DIALOUT_RESP
+Give information about call setup to driver who requested dialing out.
+.It Ar I4B_TIMEOUT_UPD
+Update the kernels timeout value(s) in case of dynamically calculated
+shorthold mode timing changes.
+.It Ar I4B_UPDOWN_IND
+Inform the kernel userland drivers about interface soft up/down status
+changes.
+.It Ar I4B_CTRL_DOWNLOAD
+Download firmware to active card(s).
+.It Ar I4B_ACTIVE_DIAGNOSTIC
+Return diagnostic information from active cards.
+.El
+.Pp
+Status and event messages available from the kernel are:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar MSG_CONNECT_IND
+An incoming call from a remote ISDN user is indicated.
+.It Ar MSG_CONNECT_ACTIVE_IND
+After an incoming call has been accepted locally or an outgoing call has
+been accepted by a remote, the exchange signaled an active connection
+and the corresponding B-channel is switched through.
+.It Ar MSG_DISCONNECT_IND
+A call was terminated.
+.It Ar MSG_DIALOUT_IND
+A userland interface driver requests the daemon to dial out (typically a
+network interface when a packet arrives in its send queue).
+.It Ar MSG_IDLE_TIMEOUT_IND
+A call was terminated by the isdn4bsd kernel driver because a B-channel
+idle timeout occurred.
+.It Ar MSG_ACCT_IND
+Accounting information from a network driver.
+.It Ar MSG_CHARGING_IND
+Charging information from the kernel.
+.El
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4bcapi.4 b/usr.sbin/i4b/man/i4bcapi.4
new file mode 100644
index 0000000..cca62c0
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bcapi.4
@@ -0,0 +1,59 @@
+.\"
+.\" Copyright (c) 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri May 25 09:38:45 2001]
+.\"
+.Dd May 21, 2001
+.Dt I4BCAPI 4
+.Os
+.Sh NAME
+.Nm i4bcapi
+.Nd CAPI driver for the isdn4bsd kernel part
+.Sh SYNOPSIS
+.Cd "device \*[q]i4bcapi\*[q]"
+.Sh DESCRIPTION
+.Nm
+is a CAPI driver for the
+.Em isdn4bsd
+package.
+It sits between layer\~4 of isdn4bsd and a driver for an active
+ISDN card; currently only the
+.Xr iavc 4
+driver for the AVM B1 and T1 family of active cards is supported.
+.Sh STANDARDS
+CAPI 2.0
+.Pq Pa http://www.capi.org/
+.Sh SEE ALSO
+.Xr iavc 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+device driver was written by
+.An Juha-Matti Liukkonen Aq jml@cubical.fi
+(Cubical Solutions Ltd, Finland).
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4bctl.4 b/usr.sbin/i4b/man/i4bctl.4
new file mode 100644
index 0000000..20b42e8
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bctl.4
@@ -0,0 +1,50 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 12:21:03 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BCTL 4
+.Os
+.Sh NAME
+.Nm i4bctl
+.Nd control device for the isdn4bsd kernel part
+.Sh SYNOPSIS
+.Cd device \&"i4bctl\&"
+.Sh DESCRIPTION
+The
+.Nm
+device is used by the
+.Xr isdndebug 8
+utility to get and set the current debugging level and other information
+of the isdn4bsd package kernel ISDN handling layers.
+.Sh SEE ALSO
+.Xr isdndebug 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@kts.org .
diff --git a/usr.sbin/i4b/man/i4bing.4 b/usr.sbin/i4b/man/i4bing.4
new file mode 100644
index 0000000..0322088
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bing.4
@@ -0,0 +1,63 @@
+.\"
+.\" Copyright (c) 2000, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 12:21:13 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BING 4
+.Os
+.Sh NAME
+.Nm i4bing
+.Nd isdn4bsd NetGraph ISDN B-channel interface driver
+.Sh SYNOPSIS
+.Cd device \&"i4bing\&" Op count
+.Sh DESCRIPTION
+The
+.Nm
+driver interfaces the
+.Fx
+NetGraph subsystem with the
+isdn4bsd package.
+.Pp
+The driver just packs packets received from the NetGraph subsystem without
+anything appended or prepended into raw HDLC packets on the B channel and
+transfers them to a remote site.
+Packets received from the remote site are queued into the NetGraph
+subsystem.
+.Pp
+In the case where a packet for a remote site arrives in the driver and no
+connection has been established yet, the driver communicates with the
+.Xr isdnd 8
+daemon to establish a connection.
+.Sh SEE ALSO
+.Xr netgraph 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4bipr.4 b/usr.sbin/i4b/man/i4bipr.4
new file mode 100644
index 0000000..6bbc1fa
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bipr.4
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 14:29:08 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BIPR 4
+.Os
+.Sh NAME
+.Nm i4bipr
+.Nd isdn4bsd IP over ISDN B-channel network driver
+.Sh SYNOPSIS
+.Cd device \&"i4bipr\&" Op count
+.Sh DESCRIPTION
+The
+.Nm
+driver interfaces the IP subsystem of the operating system with the
+isdn4bsd package so that transport of IP packets over an ISDN link
+is possible.
+.Pp
+The driver just packs IP packets without anything appended or prepended
+into raw HDLC packets on the B channel and transfers them to a remote site.
+IP packets received from the remote site are queued into the local IP
+protocol stack.
+.Pp
+The format of the resulting packet on the B channel is:
+.Pp
+.Dl (HDLC opening flag) (IP-packet) (CRC) (HDLC closing flag)
+.Pp
+In the case where an IP packet for a remote site arrives in the driver and no
+connection has been established yet, the driver communicates with the
+.Xr isdnd 8
+daemon to establish a connection.
+.Pp
+The driver has support for interfacing to the
+.Xr bpf 4
+subsystem for using
+.Xr tcpdump 1
+with the
+.Nm ipr
+interfaces.
+.Pp
+The driver optionally (when compiled with the IPR_VJ option) provides Van
+Jacobsen header compression, under control of the link0 and link1 options to
+.Xr ifconfig 8 :
+.Pp
+.Bl -tag -width 15n -offset indent -compact
+.It link0
+Apply VJ compression to outgoing packets on this interface, and assume that
+incoming packets require decompression.
+.It link1
+Check incoming packets for Van Jacobsen compression; if they appear to be
+compressed, automatically set link0.
+.El
+.Pp
+The default values are
+.Em on
+for
+.Em link1
+and
+.Em off
+for
+.Em link0 .
+.Sh SEE ALSO
+.Xr tcpdump 1 ,
+.Xr bpf 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4bisppp.4 b/usr.sbin/i4b/man/i4bisppp.4
new file mode 100644
index 0000000..0cc66f8
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bisppp.4
@@ -0,0 +1,123 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 12:43:22 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BISPPP 4
+.Os
+.Sh NAME
+.Nm i4bisppp
+.Nd isdn4bsd synchronous PPP over ISDN B-channel network driver
+.Sh SYNOPSIS
+.Cd device \&"i4bisppp\&" Op count
+.Cd device sppp
+.Sh DESCRIPTION
+The
+.Nm
+driver interfaces the IP subsystem of the operating system with the
+isdn4bsd package so that a transport of IP packets over an ISDN link
+is possible.
+.Pp
+The driver is just a glue layer between the
+.Xr sppp 4
+backend and the ISDN4BSD package and needs the
+.Xr sppp 4
+driver configured into the kernel.
+.Pp
+The network interfaces provided by this driver are named
+.Em isp<n> ,
+where <n> is a number between 0 and count-1 from the kernel config line.
+.Pp
+For configuration of the i4bsppp driver, either the
+.Xr spppcontrol 8
+utility is used or the driver can be configured via
+.Xr isdnd 1
+and its associated
+.Xr isdnd.rc 5
+file.
+.Pp
+In case an IP packet for a remote side arrives in the driver and no
+connection is established yet, the driver communicates with the
+.Xr isdnd 8
+daemon to establish a connection.
+.Pp
+The driver has support for interfacing to the
+.Xr bpf 4
+subsystem for using
+.Xr tcpdump 1
+with the
+.Nm isp
+interfaces.
+.Pp
+.Sh LINK0 and LINK1
+The
+.Em link0
+and
+.Em link1
+flags given as parameters to
+.Xr ifconfig 8
+have the following meaning for the
+.Em isp<n>
+devices:
+.Bl -tag -width link0 -compact
+.Pp
+.It Li link0
+wait passively for connection
+.Pp
+.It Li link1
+auto-dial on output
+.El
+.Pp
+The
+.Em link0
+and
+.Em link1
+flags are set to
+.Em off
+by default.
+.Pp
+See
+.Xr sppp 4
+for a more detailed discussion of the flags.
+.Sh SEE ALSO
+.Xr tcpdump 1 ,
+.Xr bpf 4 ,
+.Xr sppp 4 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8 ,
+.Xr spppcontrol 8
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+device driver was written by
+.An Joerg Wunsch Aq joerg@FreeBSD.org
+and then added to ISDN4BSD by
+.An Gary Jennejohn Aq gj@FreeBSD.org .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4bq921.4 b/usr.sbin/i4b/man/i4bq921.4
new file mode 100644
index 0000000..4eec2b3
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bq921.4
@@ -0,0 +1,48 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 12:49:44 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BQ921 4
+.Os
+.Sh NAME
+.Nm i4bq921
+.Nd isdn4bsd pseudo device driver handling the Q.921 protocol
+.Sh SYNOPSIS
+.Cd device \&"i4bq921\&"
+.Sh DESCRIPTION
+.Nm
+is the ISDN D channel layer 2 handler.
+.Sh STANDARDS
+ITU Recommendation Q.920 and Q.921
+.Sh SEE ALSO
+.Xr i4bq931 4
+.Sh AUTHORS
+The
+.Nm
+driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4bq931.4 b/usr.sbin/i4b/man/i4bq931.4
new file mode 100644
index 0000000..3bdfd6e
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bq931.4
@@ -0,0 +1,48 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 14:11:55 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BQ931 4
+.Os
+.Sh NAME
+.Nm i4bq931
+.Nd isdn4bsd pseudo device driver handling the Q.931 protocol
+.Sh SYNOPSIS
+.Cd device \&"i4bq931\&"
+.Sh DESCRIPTION
+.Nm
+is the ISDN D channel layer 3 handler.
+.Sh STANDARDS
+ITU Recommendation Q.930 and Q.931
+.Sh SEE ALSO
+.Xr i4bq921 4
+.Sh AUTHORS
+The
+.Nm
+driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4brbch.4 b/usr.sbin/i4b/man/i4brbch.4
new file mode 100644
index 0000000..aaa6044
--- /dev/null
+++ b/usr.sbin/i4b/man/i4brbch.4
@@ -0,0 +1,49 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 12:52:34 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BRBCH 4
+.Os
+.Sh NAME
+.Nm i4brbch
+.Nd isdn4bsd ISDN Raw B-CHannel access driver
+.Sh SYNOPSIS
+.Cd device \&"i4brbch\&" Op count
+.Sh DESCRIPTION
+The
+.Nm
+driver provides an interface to the raw untranslated B-channel.
+It is part of the isdn4bsd package.
+.Sh SEE ALSO
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4btel.4 b/usr.sbin/i4b/man/i4btel.4
new file mode 100644
index 0000000..dcbb05a
--- /dev/null
+++ b/usr.sbin/i4b/man/i4btel.4
@@ -0,0 +1,133 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 14:14:06 2002]
+.\"
+.Dd July 28, 2002
+.Dt I4BTEL 4
+.Os
+.Sh NAME
+.Nm i4btel
+.Nd isdn4bsd ISDN B-channel telephony interface driver
+.Sh SYNOPSIS
+.Cd device \&"i4btel\&" Op count
+.Sh DESCRIPTION
+The
+.Nm
+driver provides an interface to the ISDN B-channel for telephony applications
+and is currently used by the
+.Xr isdnd 8
+for answering machine support.
+The driver is part of the isdn4bsd package.
+.Pp
+The lower six bits of the drivers's minor number is used to specify a
+unit number, whereas the upper two bits specify a functionality.
+.Pp
+Functionality zero is the usual telephony data stream i/o driver.
+.Pp
+Functionality one is used to enable commands to dial out and hang up and
+receive responses about the state of the dial out progress and status.
+This commands may change in the future, for details see the file
+.Em i4b_tel_ioctl.h
+and the
+.Xr isdnphone 1
+utility.
+.Pp
+The telephony data stream comes out of the line in a bit-reversed format,
+so the
+.Nm
+driver does the bit-reversion process in any case.
+.Pp
+Additionally, the user can specify to do A-law to u-law, u-law to A-law
+or no conversion at all in the i4btel driver by using the
+.Xr isdntelctl 8
+utility.
+.Pp
+The driver is able to process several ioctl's:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar I4B_TEL_GETAUDIOFMT
+get currently used audio format conversion.
+.It Ar I4B_TEL_SETAUDIOFMT
+set currently used audio format conversion.
+.It Ar I4B_TEL_EMPTYINPUTQUEUE
+clear the input queue.
+.El
+.Pp
+For the I4B_TEL_GETAUDIOFMT and I4B_TEL_SETAUDIOFMT, the following
+parameters are available:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar CVT_NONE
+do no A-law/u-law audio format conversion.
+The conversion path looks like this:
+.Pp
+USER <--> bitreversing <--> ISDN-line
+.Pp
+.It Ar CVT_ALAW2ULAW
+set set audio format conversion to do an audio conversion from A-law
+(on the ISDN line) to u-law (in the userland).
+The
+.Xr read 2
+conversion path looks like this:
+.Pp
+USER <-- u-law/A-law <-- bitreversing <-- ISDN-line
+.Pp
+and the
+.Xr write 2
+conversion path looks like this:
+.Pp
+USER --> u-law/A-law --> bitreversing --> ISDN-line
+.Pp
+.It Ar CVT_ULAW2ALAW
+set set audio format conversion to do an audio conversion from u-law
+(on the ISDN line) to A-law (in the userland).
+The
+.Xr read 2
+conversion path looks like this:
+.Pp
+USER <-- A-law/u-law <-- bitreversing <-- ISDN-line
+.Pp
+and the
+.Xr write 2
+conversion path looks like this:
+.Pp
+USER --> A-law/u-law --> bitreversing --> ISDN-line
+.Pp
+.El
+.Sh STANDARDS
+A-Law and u-Law are specified in ITU Recommendation G.711.
+.Sh SEE ALSO
+.Xr g711conv 1 ,
+.Xr isdnphone 1 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd 8 ,
+.Xr isdntelctl 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/i4btrc.4 b/usr.sbin/i4b/man/i4btrc.4
new file mode 100644
index 0000000..cbd6a7f
--- /dev/null
+++ b/usr.sbin/i4b/man/i4btrc.4
@@ -0,0 +1,54 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 14:21:03 2002]
+.\"
+.Dd July 30, 1999
+.Dt I4BTRC 4
+.Os
+.Sh NAME
+.Nm i4btrc
+.Nd "isdn4bsd ISDN interface driver for D and B channel tracing
+.Sh SYNOPSIS
+.Cd device \&"i4btrc\&" Op count
+.Sh DESCRIPTION
+The
+.Nm
+driver is used to add a header to the data got from the D and/or B channel
+and queues it to be read and further processed by the
+.Xr isdntrace 8
+or
+.Xr isdndecode 8
+utilities.
+.Sh SEE ALSO
+.Xr isdnd 8 ,
+.Xr isdndecode 8 ,
+.Xr isdntrace 8
+.Sh AUTHORS
+The
+.Nm
+device driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/iavc.4 b/usr.sbin/i4b/man/iavc.4
new file mode 100644
index 0000000..93e5cdf
--- /dev/null
+++ b/usr.sbin/i4b/man/iavc.4
@@ -0,0 +1,73 @@
+.\"
+.\" Copyright (c) 2001, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 16:37:51 2002]
+.\"
+.Dd July 28, 2002
+.Dt IAVC 4
+.Os
+.Sh NAME
+.Nm iavc
+.Nd isdn4bsd AVM B1/T1 driver
+.Sh SYNOPSIS
+.Cd "device iavc"
+.Cd device \&"i4bcapi\&"
+.Pp
+NOTE:
+For the B1 ISA card, something like
+.Pp
+.Bd -literal -offset indent
+hint.iavc.0.at="isa"
+hint.iavc.0.port="0x150"
+hint.iavc.0.irq="5"
+.Ed
+.Pp
+must be added to
+.Pa /boot/device.hints
+.Sh DESCRIPTION
+The
+.Nm
+driver is used to access the AVM family of active cards to the
+.Xr i4bcapi 4
+driver and the
+.Em isdn4bsd
+package.
+Currently the AVM B1 PCI, the AVM B1 ISA and the AVM T1 PCI
+cards are supported.
+.Sh STANDARDS
+CAPI 2.0
+.Pq Pa http://www.capi.org/
+.Sh SEE ALSO
+.Xr i4bcapi 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+device driver was written by
+.An Juha-Matti Liukkonen Aq jml@cubical.fi
+(Cubical Solutions Ltd, Finland).
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/ifpi.4 b/usr.sbin/i4b/man/ifpi.4
new file mode 100644
index 0000000..aa67b4a
--- /dev/null
+++ b/usr.sbin/i4b/man/ifpi.4
@@ -0,0 +1,64 @@
+.\"
+.\" Copyright (c) 2000, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 16:38:40 2002]
+.\"
+.Dd July 28, 2002
+.Dt IFPI 4
+.Os
+.Sh NAME
+.Nm ifpi
+.Nd isdn4bsd AVM Fritz!Card PCI Version 1 driver
+.Sh SYNOPSIS
+.Cd device ifpi
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 support as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive PCI ISDN Version 1.0 cards from AVM based on the
+proprietary B-channel controller and PCI interface chip from AVM, Berlin,
+Germany.
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4 ,
+.Xr ifpi2 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Gary Jennejohn Aq gj@FreeBSD.org .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/ifpi2.4 b/usr.sbin/i4b/man/ifpi2.4
new file mode 100644
index 0000000..474dff8
--- /dev/null
+++ b/usr.sbin/i4b/man/ifpi2.4
@@ -0,0 +1,60 @@
+.\"
+.\" Copyright (c) 2001 Gary Jennejohn. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 16:38:22 2002]
+.\"
+.Dd July 28, 2002
+.Dt IFPI2 4
+.Os
+.Sh NAME
+.Nm ifpi2
+.Nd isdn4bsd AVM Fritz!Card PCI Version 2 driver
+.Sh SYNOPSIS
+.Cd device \&"ifpi2\&"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 support as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports the version 2 passive PCI ISDN cards from AVM based on
+the proprietary B-channel controller and PCI interface chip from AVM, Berlin,
+Germany.
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4 ,
+.Xr ifpi 4
+.Sh AUTHORS
+The
+.Nm
+driver and manpage were written by
+.An Gary Jennejohn Aq gj@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/ifpnp.4 b/usr.sbin/i4b/man/ifpnp.4
new file mode 100644
index 0000000..45cf413
--- /dev/null
+++ b/usr.sbin/i4b/man/ifpnp.4
@@ -0,0 +1,67 @@
+.\"
+.\" Copyright (c) 2000 Udo Schweigert. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Ust: src/i4b/man/ifpnp.4,v 1.4 2000/04/18 08:26:31 ust Exp $
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 16:39:35 2002]
+.\"
+.Dd July 28, 2002
+.Dt IFPNP 4
+.Os
+.Sh NAME
+.Nm ifpnp
+.Nd isdn4bsd AVM Fritz!Card ISA PnP driver
+.Sh SYNOPSIS
+.Cd "device ifpnp"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive ISA PnP ISDN cards from AVM, based on the
+proprietary B-channel controller and ISA PnP interface chip from
+AVM, Berlin, Germany.
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Udo Schweigert Aq ust@cert.siemens.de .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org
+and
+.An Udo Schweigert .
diff --git a/usr.sbin/i4b/man/ihfc.4 b/usr.sbin/i4b/man/ihfc.4
new file mode 100644
index 0000000..73c4f83
--- /dev/null
+++ b/usr.sbin/i4b/man/ihfc.4
@@ -0,0 +1,71 @@
+.\"
+.\" Copyright (c) 2000, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 16:47:55 2002]
+.\"
+.Dd July 28, 2002
+.Dt IHFC 4
+.Os
+.Sh NAME
+.Nm ihfc
+.Nd isdn4bsd ISA Cologne Chip HFC[-S][-SP] 2B[DS0] driver
+.Sh SYNOPSIS
+.Cd "device ihfc"
+.Pp
+NOTE:
+.Pp
+Something like:
+.Li hint.ihfc.0.at="isa"
+must be added to
+.Pa /boot/device.hints
+The driver will automatically probe for io-base and irq.
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports some passive ISA ISDN cards based on the
+HFC[-S][-SP] 2B[DS0] chips from Cologne Chip, currently this are the
+Teles 16.3c ISA PnP, the AcerISDN P10 ISA PnP and the TELEINT ISDN SPEED No.1.
+.Sh CAVEATS
+The driver is still in an experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Hans Petter Selasky Aq hselasky@c2i.net .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/isic.4 b/usr.sbin/i4b/man/isic.4
new file mode 100644
index 0000000..01a0148
--- /dev/null
+++ b/usr.sbin/i4b/man/isic.4
@@ -0,0 +1,359 @@
+.\"
+.\" Copyright (c) 1997, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 20:14:24 2002]
+.\"
+.Dd July 28, 2002
+.Dt ISIC 4
+.Os
+.Sh NAME
+.Nm isic
+.Nd isdn4bsd Siemens ISDN Chipset device driver
+.Sh SYNOPSIS
+.Cd "device isic"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports several 8 and 16bit passive ISA, ISA PnP and PCI
+ISDN cards from various manufacturers which are all based upon the
+popular Siemens ISDN chipset consisting of the ISDN Subscriber Access
+Controller ISAC (such as the PEB2085 or PSB 2186) and the High-Level
+Serial Communications Controller Extended HSCX (such as the SAB82525
+or PSB21525).
+The newer IPAC chip (which integrates an ISAC and a HSCX in one chip,
+with the added benefit of larger FIFO buffers) is also supported.
+.Sh SUPPORTED CARDS: ISA bus, NON-PnP
+.Bl -tag -width Ds -compact
+.It Ar Teles S0/8, Dr. Neuhaus Niccy 1008, Creatix ISDN-S0/8
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options TEL_S0_8
+.Ed
+.Pp
+The required entry in
+.Xr device.hints 5
+file is:
+.Pp
+.Bd -literal -offset indent
+hint.isic.0.at="isa"
+hint.isic.0.maddr="0xd0000"
+hint.isic.0.irq="5"
+hint.isic.0.flags="1"
+.Ed
+.Pp
+Notice that this cards must not have a
+.Em port
+value.
+.Pp
+Valid values for hint.isic.N.irq are 2, 3, 4, 5, 6 and 7.
+.Pp
+The i/o ports are memory mapped and the memory start address (hint.isic.N.maddr)
+may be in the range 0xA0000 to 0xDF000 and use a region of 4kB of memory.
+.Pp
+.It Ar Teles S0/16, Creatix ISDN-S0, Dr. Neuhaus Niccy 1016
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options TEL_S0_16
+.Ed
+.Pp
+The required entry in
+.Xr device.hints 5
+file is:
+.Pp
+.Bd -literal -offset indent
+hint.isic.0.at="isa"
+hint.isic.0.port="0xd80"
+hint.isic.0.maddr="0xd0000"
+hint.isic.0.irq="5"
+hint.isic.0.flags="2"
+.Ed
+.Pp
+These boards have a jumper which specifies an i/o base address
+(hint.isic.N.port) of either 0xd80, 0xe80 or 0xf80.
+The remaining necessary configuration values are then
+programmed at run time by accessing this i/o port.
+.Pp
+Valid interrupts (hint.isic.N.irq) are 2, 3, 4, 5, 10, 11, 12 or 15.
+.Pp
+Valid memory start addresses (hint.isic.N.maddr) are
+0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xCE000,
+0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000 and
+0xDE000.
+.Pp
+Notice: Although the jumpers are labeled 0xd80, 0xe80 or 0xf80, they
+also require i/o space at addresses 0x180, 0x280 or 0x380.
+.Pp
+.It Ar Teles S0/16.3
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options TEL_S0_16_3
+.Ed
+.Pp
+The required entry in
+.Xr device.hints 5
+file is:
+.Pp
+.Bd -literal -offset indent
+hint.isic.0.at="isa"
+hint.isic.0.port="0xd80"
+hint.isic.0.irq="5"
+hint.isic.0.flags="3"
+.Ed
+.Pp
+This card is completely i/o mapped and must not have an
+.Em maddr
+statement in the hints file.
+.Pp
+Valid interrupts (hint.isic.N.irq) are 2, 5, 9, 10, 12 or 15.
+.Pp
+These boards have a jumper which specifies an i/o base address
+(hint.isic.N.port) of either 0xd80, 0xe80 or 0xf80.
+.Pp
+Notice: Although the switch positions are labeled 0x180, 0x280 and 0x380,
+the card is to be configured at 0xd80, 0xe80 or 0xf80 respectively!
+.Pp
+.It Ar AVM A1 and AVM Fritz!Card
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options AVM_A1
+.Ed
+.Pp
+The required entry in
+.Xr device.hints 5
+file is:
+.Pp
+.Bd -literal -offset indent
+hint.isic.0.at="isa"
+hint.isic.0.port="0x340"
+hint.isic.0.irq="5"
+hint.isic.0.flags="4"
+.Ed
+.Pp
+These boards have a jumper which specifies an i/o base address (hint.isic.N.port)
+of either 0x200, 0x240, 0x300 or 0x340.
+.Pp
+Valid interrupt (hint.isic.N.irq) values are 3, 4, 5, 6, 7, 10, 11, 12 or 15.
+.Pp
+Older Versions of the AVM A1 also require setting of an IRQ jumper, newer
+versions of this and the Fritz!Card only have an i/o base jumper and the
+interrupt is setup at runtime by reprogramming a register.
+.Pp
+.It Ar USRobotics Sportster ISDN TA intern and Stollmann Tina pp
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options USR_STI
+.Ed
+.Pp
+The required entry in
+.Xr device.hints 5
+file is:
+.Pp
+.Bd -literal -offset indent
+hint.isic.0.at="isa"
+hint.isic.0.port="0x268"
+hint.isic.0.irq="5"
+hint.isic.0.flags="7"
+.Ed
+.Pp
+Valid i/o port values (hint.isic.N.port) are 0x200, 0x208, 0x210, 0x218,
+0x220, 0x228, 0x230, 0x238, 0x240, 0x248, 0x250, 0x258, 0x260, 0x268,
+0x270 and 0x278.
+.Pp
+Valid interrupt (hint.isic.N.irq) values are 5, 7, 10, 11, 12, 14, 15.
+.Pp
+Notice: this card has a strange address decoding scheme resulting in
+occupying 64 windows of some bytes length over the whole i/o address
+range.
+.Pp
+.It Ar ITK ix1 Micro ( < V.3, non-PnP version )
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options ITKIX1
+.Ed
+.Pp
+The required entry in
+.Xr device.hints 5
+file is:
+.Pp
+.Bd -literal -offset indent
+hint.isic.0.at="isa"
+hint.isic.0.port="0x398"
+hint.isic.0.irq="10"
+hint.isic.0.flags="18"
+.Ed
+.Pp
+Valid i/o port values must be in the range (<unknown>).
+.Pp
+Valid interrupt configurations are (<unknown>).
+.Pp
+.It Ar ELSA MicroLink ISDN/PCC-16 and ELSA PCFpro
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options ELSA_PCC16
+.Ed
+.Pp
+The required entry in
+.Xr device.hints 5
+file is:
+.Pp
+.Bd -literal -offset indent
+hint.isic.0.at="isa"
+hint.isic.0.port="0x360"
+hint.isic.0.irq="10"
+hint.isic.0.flags="20"
+.Ed
+.Pp
+Valid i/o port (hint.isic.N.port) values are 0x160, 0x170, 0x260 and 0x360.
+.Pp
+Valid interrupt (hint.isic.N.irq) values are 2, 3, 5, 10, 11 and 15.
+.Pp
+The LED's are not supported and are off.
+.Pp
+.El
+.Sh SUPPORTED CARDS: ISA bus, PnP (Plug 'n Pray)
+.Bl -tag -width Ds -compact
+.It Ar Teles S0/16.3 PnP
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options TEL_S0_16_3_P
+.Ed
+.Pp
+.It Ar Creatix ISDN-S0 P&P
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options CRTX_S0_P
+.Ed
+.Pp
+.It Ar "Dr. Neuhaus Niccy Go@"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options DRN_NGO
+.Ed
+.Pp
+.It Ar "Sedlbauer Win Speed"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options SEDLBAUER
+.Ed
+.Pp
+.It Ar "Dynalink IS64PH"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options DYNALINK
+.Ed
+.Pp
+.It Ar "ELSA QuickStep 1000pro ISA"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options ELSA_QS1ISA
+.Ed
+.Pp
+The LED's are not supported and are off.
+.Pp
+.It Ar "Siemens I-Surf 2.0"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options SIEMENS_ISURF2
+.Ed
+.Pp
+.It Ar "Asuscom ISDNlink 128K ISA"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options ASUSCOM_IPAC
+.Ed
+.Pp
+.It Ar "Eicon Diehl DIVA 2.0 and 2.02"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options EICON_DIVA
+.Ed
+.Pp
+.It Ar "Compaq Microcom 610 ISDN card (Compaq series PSB2222I)"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options COMPAQ_M610
+.Ed
+.Pp
+.El
+.Sh SUPPORTED CARDS: PCI bus
+.Bl -tag -width Ds -compact
+.It Ar "ELSA QuickStep 1000pro-PCI"
+.Pp
+The required entry in the kernel config file is:
+.Bd -literal -offset indent
+options ELSA_QS1PCI
+.Ed
+.Pp
+The LED's are not supported and are off.
+.Pp
+.El
+.Sh CAVEATS
+Note that all of ISA boards with i/o ports actually use several ranges
+of port addresses; Teles happen to refer to the 0xd80 range in their
+documentation (the board also uses 0x180 etc.), while AVM happen to refer
+to the 0x200 range in their documentation (the board also uses 0x600 etc.)
+The driver matches the manufacturers' description for the purposes of
+configuration, but of course makes use of all the ports in order to
+operate the card.
+.Sh BUGS
+Since there is no hardware documentation available from the manufacturers
+of several boards, it is likely that there are many bugs left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver and this manpage were written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
+It is based on earlier work of
+.An Arne Helme ,
+.An Andrew Gordon
+and
+.An Gary Jennejohn Aq gj@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/itjc.4 b/usr.sbin/i4b/man/itjc.4
new file mode 100644
index 0000000..8469fe9
--- /dev/null
+++ b/usr.sbin/i4b/man/itjc.4
@@ -0,0 +1,66 @@
+.\"
+.\" Copyright (c) 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri Jan 12 10:07:04 2001]
+.\"
+.Dd January 12, 2001
+.Dt ITJC 4
+.Os
+.Sh NAME
+.Nm itjc
+.Nd isdn4bsd NETjet-S / Teles PCI-TJ hardware driver
+.Sh SYNOPSIS
+.Cd "device itjc"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive PCI ISDN cards based on the combination of
+the Siemens/Infineon ISAC chip and the Tiger Jet Network Inc. Tiger 300/320
+chip.
+.Pp
+Currently supported cards are the Traverse Technologies NETjet-S PCI ISDN
+card and the Teles PCI-TJ.
+.Sh CAVEATS
+The driver is in an experimental state.
+.Sh BUGS
+Always at least one left.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Sergio de Souza Prallon Aq prallon@tmp.com.br .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/i4b/man/iwic.4 b/usr.sbin/i4b/man/iwic.4
new file mode 100644
index 0000000..8f6674a
--- /dev/null
+++ b/usr.sbin/i4b/man/iwic.4
@@ -0,0 +1,68 @@
+.\"
+.\" Copyright (c) 2000, 2002 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Sun Jul 28 16:49:47 2002]
+.\"
+.Dd July 28, 2002
+.Dt IWIC 4
+.Os
+.Sh NAME
+.Nm iwic
+.Nd isdn4bsd Winbond ISDN Chip device driver
+.Sh SYNOPSIS
+.Cd "device iwic0"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides D-channel layer 1 supports as specified in ITU Recommendation
+I.430 and layer 1 support for the B-channel.
+.Pp
+The driver supports passive PCI ISDN cards from various manufacturers
+based on the Winbond W6692 chip.
+.Sh SUPPORTED CARDS
+.Bl -tag -width Ds -compact
+.It Ar ASUSCOM P-IN100-ST-D
+.Pp
+.It Ar Dynalink IS64PPH
+.El
+.Sh CAVEATS
+The driver is still in a somewhat experimental state.
+.Sh BUGS
+Layer 1 persistent deactivation not yet implemented.
+.Sh STANDARDS
+CCITT Recommendation I.430
+.Sh SEE ALSO
+.Xr i4bq921 4 ,
+.Xr i4bq931 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Dave Boyce Aq dave@abyss.demon.co.uk .
+.Pp
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@FreeBSD.org .
diff --git a/usr.sbin/ifmcstat/Makefile b/usr.sbin/ifmcstat/Makefile
new file mode 100644
index 0000000..4d6cc88
--- /dev/null
+++ b/usr.sbin/ifmcstat/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= ifmcstat
+MAN= ifmcstat.8
+BINMODE= 550
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ifmcstat/ifmcstat.8 b/usr.sbin/ifmcstat/ifmcstat.8
new file mode 100644
index 0000000..cc2a92e
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.8
@@ -0,0 +1,58 @@
+.\" $KAME: ifmcstat.8,v 1.3 2000/10/15 11:43:57 itojun Exp $
+.\"
+.\" Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 21, 1998
+.Dt IFMCSTAT 8
+.Os
+.Sh NAME
+.Nm ifmcstat
+.Nd dump multicast group management statistics per interface
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility dumps multicast group information from the kernel.
+It is similar but not identical to the output from
+.Nm netstat Fl ina .
+.Pp
+There are no command line options.
+.Pp
+Only root can use
+.Nm .
+.Sh SEE ALSO
+.Xr netstat 1
+.Rs
+.%A G. Malkin
+.%A R. Minnear
+.%R IPng for IPv6
+.%N RFC2080
+.Re
diff --git a/usr.sbin/ifmcstat/ifmcstat.c b/usr.sbin/ifmcstat/ifmcstat.c
new file mode 100644
index 0000000..2173999
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.c
@@ -0,0 +1,266 @@
+/*
+ * 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 <stdlib.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <string.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#define _KERNEL
+#include <netinet/if_ether.h>
+#undef _KERNEL
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+
+kvm_t *kvmd;
+
+struct nlist nl[] = {
+#define N_IFNET 0
+ { "_ifnet" },
+ { "" },
+};
+
+const char *inet6_n2a __P((struct in6_addr *));
+int main __P((void));
+char *ifname __P((struct ifnet *));
+void kread __P((u_long, void *, int));
+void if6_addrlist __P((struct ifaddr *));
+void in6_multilist __P((struct in6_multi *));
+struct in6_multi * in6_multientry __P((struct in6_multi *));
+
+#define KREAD(addr, buf, type) \
+ kread((u_long)addr, (void *)buf, sizeof(type))
+
+#ifdef N_IN6_MK
+struct multi6_kludge {
+ LIST_ENTRY(multi6_kludge) mk_entry;
+ struct ifnet *mk_ifp;
+ struct in6_multihead mk_head;
+};
+#endif
+
+const char *inet6_n2a(p)
+ 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)) {
+ 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)";
+}
+
+int main()
+{
+ char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
+ struct ifnet *ifp, *nifp, ifnet;
+
+ if ((kvmd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf)) == NULL) {
+ perror("kvm_openfiles");
+ exit(1);
+ }
+ if (kvm_nlist(kvmd, nl) < 0) {
+ perror("kvm_nlist");
+ exit(1);
+ }
+ if (nl[N_IFNET].n_value == 0) {
+ printf("symbol %s not found\n", nl[N_IFNET].n_name);
+ exit(1);
+ }
+ KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
+ while (ifp) {
+ KREAD(ifp, &ifnet, struct ifnet);
+ printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
+
+ if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+ nifp = TAILQ_NEXT(&ifnet, if_link);
+
+ /* not supported */
+
+ ifp = nifp;
+ }
+
+ exit(0);
+ /*NOTREACHED*/
+}
+
+char *ifname(ifp)
+ struct ifnet *ifp;
+{
+ static char buf[BUFSIZ];
+ struct ifnet ifnet;
+ char ifnamebuf[IFNAMSIZ];
+
+ KREAD(ifp, &ifnet, struct ifnet);
+ strlcpy(buf, ifnet.if_xname, sizeof(buf));
+ return buf;
+}
+
+void kread(addr, buf, len)
+ u_long addr;
+ void *buf;
+ int len;
+{
+ if (kvm_read(kvmd, addr, buf, len) != len) {
+ perror("kvm_read");
+ exit(1);
+ }
+}
+
+void
+if6_addrlist(ifap)
+ struct ifaddr *ifap;
+{
+ struct ifaddr ifa;
+ struct sockaddr sa;
+ struct in6_ifaddr if6a;
+ struct ifaddr *ifap0;
+
+ 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 = TAILQ_NEXT(&ifa, ifa_link);
+ }
+ 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 multicnt %d\n",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
+ ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+#ifdef N_IN6_MK
+ if (nl[N_IN6_MK].n_value != 0) {
+ LIST_HEAD(in6_mktype, multi6_kludge) in6_mk;
+ struct multi6_kludge *mkp, mk;
+ char *nam;
+
+ KREAD(nl[N_IN6_MK].n_value, &in6_mk, struct in6_mktype);
+ KREAD(ifap0, &ifa, struct ifaddr);
+
+ nam = strdup(ifname(ifa.ifa_ifp));
+ if (!nam) {
+ fprintf(stderr, "ifmcstat: not enough core\n");
+ exit(1);
+ }
+
+ LIST_FOREACH(mkp, &in6_mk, mk_entry) {
+ KREAD(mkp, &mk, struct multi6_kludge);
+ if (strcmp(nam, ifname(mk.mk_ifp)) == 0 &&
+ LIST_FIRST(&mk.mk_head)) {
+ printf("\t(on kludge entry for %s)\n", nam);
+ in6_multilist(LIST_FIRST(&mk.mk_head));
+ }
+ }
+
+ free(nam);
+ }
+#endif
+}
+
+struct in6_multi *
+in6_multientry(mc)
+ 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(LIST_NEXT(&multi, in6m_entry));
+}
+
+void
+in6_multilist(mc)
+ struct in6_multi *mc;
+{
+ while (mc)
+ mc = in6_multientry(mc);
+}
diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile
new file mode 100644
index 0000000..afe4c51
--- /dev/null
+++ b/usr.sbin/inetd/Makefile
@@ -0,0 +1,22 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= inetd
+MAN= inetd.8
+MLINKS= inetd.8 inetd.conf.5
+SRCS= inetd.c builtins.c
+
+WARNS?= 2
+CFLAGS+= -DLOGIN_CAP
+#CFLAGS+= -DSANITY_CHECK
+
+DPADD= ${LIBUTIL} ${LIBWRAP}
+LDADD= -lutil -lwrap
+
+.if !defined(RELEASE_CRUNCH)
+CFLAGS+= -DINET6 -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..96eeb33
--- /dev/null
+++ b/usr.sbin/inetd/builtins.c
@@ -0,0 +1,817 @@
+/*-
+ * 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);
+unsigned long 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 */
+/* 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 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.
+ */
+
+unsigned long
+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 ((u_long)25567 * 24*60*60)
+ return (htonl((long)(tv.tv_sec + OFFSET)));
+#undef OFFSET
+}
+
+/* ARGSUSED */
+void
+machtime_dg(int s, struct servtab *sep)
+{
+ unsigned long 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)
+{
+ unsigned long 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..fd4f4a2
--- /dev/null
+++ b/usr.sbin/inetd/inetd.8
@@ -0,0 +1,921 @@
+.\" 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.
+.\" 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: @(#)inetd.8 8.3 (Berkeley) 4/13/94
+.\" $FreeBSD$
+.\"
+.Dd February 7, 1996
+.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 the
+.Xr jail 8
+environment.
+.Pp
+When 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
+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 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.
+If it is desired that the service is reachable via T/TCP, one should
+specify
+.Dq tcp/ttcp ,
+which implies IPv4 for backward compatibility.
+The name
+.Dq tcp4/ttcp
+specifies IPv4 only, while
+.Dq tcp6/ttcp
+specifies IPv6 only.
+The name
+.Dq tcp46/ttcp
+specify that the entry accepts both IPv6 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 ,
+.Pq Xr biff 1
+and
+.Xr talkd 8
+utilities are both 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.
+Optional
+.Em group
+part separated by
+.Dq \&:
+allows to specify group name different
+than default group for this user.
+Optional
+.Em login-class
+part separated by
+.Dq /
+allows to specify login class different
+than 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
+should be just as arguments
+normally are, starting with argv[0], which 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,
+.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 TCPMUX services 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
+.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 ,
+instead use
+.Pa /var/run
+or a similar directory.
+.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.
+.Sh "FILES"
+.Bl -tag -width /var/run/inetd.pid -compact
+.It Pa /etc/inetd.conf
+configuration file
+.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.
+.El
+.Sh SEE ALSO
+.Xr ipsec_set_policy 3 ,
+.Xr hosts_access 5 ,
+.Xr hosts_options 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr rpc 5 ,
+.Xr services 5 ,
+.Xr comsat 8 ,
+.Xr fingerd 8 ,
+.Xr ftpd 8 ,
+.Xr rexecd 8 ,
+.Xr rlogind 8 ,
+.Xr rpcbind 8 ,
+.Xr rshd 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..a6fa37a
--- /dev/null
+++ b/usr.sbin/inetd/inetd.c
@@ -0,0 +1,2591 @@
+/*
+ * 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.
+ * 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, 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,ttcp], udp[4][6], unix
+ * wait/nowait single-threaded/multi-threaded
+ * user user 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 user 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 <netinet6/ipsec.h>
+#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */
+#undef IPSEC
+#endif
+#endif
+
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#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 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) {
+ FILE *fp;
+ 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.. */
+ }
+ pid = getpid();
+ fp = fopen(pid_file, "w");
+ if (fp) {
+ fprintf(fp, "%ld\n", (long)pid);
+ fclose(fp);
+ } else {
+ syslog(LOG_WARNING, "%s: %m", pid_file);
+ }
+ }
+
+ 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", __FUNCTION__);
+ 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|
+ NI_WITHSCOPEID);
+ }
+ } else {
+ getnameinfo((struct sockaddr *)&peermax,
+ peer.sa_len,
+ pname, sizeof(pname),
+ NULL, 0,
+ NI_NUMERICHOST|
+ NI_WITHSCOPEID);
+ }
+ 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) {
+ 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",
+ __FUNCTION__, 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
+ if (sep->se_type == TTCP_TYPE)
+ if (setsockopt(sep->se_fd, IPPROTO_TCP, TCP_NOPUSH,
+ (char *)&on, sizeof (on)) < 0)
+ syslog(LOG_ERR, "setsockopt (TCP_NOPUSH): %m");
+#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", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not off", __FUNCTION__, 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", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (!FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not on", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (nsock == 0) {
+ syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
+ 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, "ttcp") == 0)
+ sep->se_type = TTCP_TYPE;
+ else 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|NI_WITHSCOPEID);
+ (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|NI_WITHSCOPEID);
+ 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 &&
+ 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|NI_WITHSCOPEID);
+ 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 | NI_WITHSCOPEID) != 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 | NI_WITHSCOPEID) == 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 | NI_WITHSCOPEID);
+ 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..3293364
--- /dev/null
+++ b/usr.sbin/inetd/inetd.h
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ * 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$
+ */
+
+#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 TTCP_TYPE 3
+#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)
+#define ISTTCP(sep) ((sep)->se_type == TTCP_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..fc4ff5a
--- /dev/null
+++ b/usr.sbin/inetd/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * 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
+ * $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..b778713
--- /dev/null
+++ b/usr.sbin/iostat/iostat.8
@@ -0,0 +1,434 @@
+.\"
+.\" 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 December 22, 1997
+.Dt IOSTAT 8
+.Os
+.Sh NAME
+.Nm iostat
+.Nd report
+.Tn I/O
+statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl CdhKIoT?\&
+.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 aren't
+.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 ?\&
+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 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 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 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.
+.Sh AUTHORS
+.An Kenneth Merry Aq ken@FreeBSD.org
diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c
new file mode 100644
index 0000000..724756e
--- /dev/null
+++ b/usr.sbin/iostat/iostat.c
@@ -0,0 +1,763 @@
+/*
+ * 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 <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_CP_TIME 2
+ { "_cp_time" },
+#define X_BOOTTIME 3
+ { "_boottime" },
+#define X_END 3
+ { NULL },
+};
+
+struct statinfo cur, last;
+int num_devices;
+struct device_selection *dev_select;
+int maxshowdevs;
+volatile sig_atomic_t headercount;
+int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
+
+/* local function declarations */
+static void usage(void);
+static void needhdr(int signo);
+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 [-CdhIKoT?] [-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;
+ register int 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:?")) != -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;
+ 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) {
+ Cflag = 1;
+ Tflag = 1;
+ }
+
+ /*
+ * Figure out how many devices we should display.
+ */
+ if (nflag == 0) {
+ 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;
+ }
+ }
+
+ /* find out how many devices we have */
+ if ((num_devices = devstat_getnumdevs(kd)) < 0)
+ err(1, "can't get number of devices");
+
+ 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);
+
+ 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 (readvar(kd, "kern.cp_time", X_CP_TIME,
+ &cur.cp_time, sizeof(cur.cp_time)) != 0) {
+ Cflag = 0;
+ warnx("disabling CPU time statistics");
+ }
+ }
+
+ if (!--headercount) {
+ phdr();
+ headercount = 20;
+ }
+
+ 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();
+ headercount = 20;
+ 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();
+ headercount = 20;
+ 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 (Tflag > 0)
+ printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
+ cur.tk_nout/etime);
+
+ devstats(hflag, etime, havelast);
+
+ 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;
+}
+
+static void
+phdr(void)
+{
+ register int i;
+ int printed;
+
+ 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;
+ if (oflag > 0)
+ (void)printf("%12.6s%d ",
+ cur.dinfo->devices[di].device_name,
+ cur.dinfo->devices[di].unit_number);
+ else
+ printf("%15.6s%d ",
+ cur.dinfo->devices[di].device_name,
+ cur.dinfo->devices[di].unit_number);
+ 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)
+{
+ register int dn;
+ long double transfers_per_second;
+ long double kb_per_transfer, mb_per_second;
+ u_int64_t total_bytes, total_transfers, total_blocks;
+ long double total_mb;
+ long double blocks_per_second, ms_per_transaction;
+
+ 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_TRANSFERS, &total_transfers,
+ DSM_TOTAL_BLOCKS, &total_blocks,
+ DSM_KB_PER_TRANSFER, &kb_per_transfer,
+ DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
+ DSM_MB_PER_SECOND, &mb_per_second,
+ DSM_BLOCKS_PER_SECOND, &blocks_per_second,
+ DSM_MS_PER_TRANSACTION, &ms_per_transaction,
+ 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) {
+ int block_size = cur.dinfo->devices[di].block_size;
+ total_blocks = total_blocks * (block_size ?
+ block_size : 512) / 1024;
+ }
+
+ 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);
+ }
+ }
+ }
+}
+
+static void
+cpustats(void)
+{
+ register 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, nlid, ptr, len);
+
+ if (nbytes == 0) {
+ warnx("kvm_read(%s): %s", name, kvm_geterr(kd));
+ return (1);
+ }
+ if (nbytes != len) {
+ warnx("kvm_read(%s): expected %lu bytes, got %ld bytes",
+ name, (unsigned long)len, (long)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..018743b
--- /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 DIAGNOSTICS
+.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..12802b4
--- /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 __P((void));
+static void get_policy __P((void));
+static void dump_policy __P((void));
+static int mask2plen __P((struct sockaddr_in6 *));
+static int parse_prefix __P((const char *, struct in6_addrpolicy *));
+static void make_policy_fromfile __P((char *));
+static void plen2mask __P((struct sockaddr_in6 *, int));
+static void set_policy __P((void));
+static void add_policy __P((char *, char *, char *));
+static void delete_policy __P((char *));
+static void flush_policy __P(());
+
+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/ipftest/Makefile b/usr.sbin/ipftest/Makefile
new file mode 100644
index 0000000..fa34970
--- /dev/null
+++ b/usr.sbin/ipftest/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+MAINTAINER= darrenr@freebsd.org
+
+.PATH: ${.CURDIR}/../../sys/contrib/ipfilter/netinet \
+ ${.CURDIR}/../../contrib/ipfilter ${.CURDIR}/../../contrib/ipfilter/man
+
+PROG= ipftest
+SRCS= ipt.c parse.c fil.c ipft_sn.c ipft_ef.c ipft_td.c ipft_pc.c opt.c \
+ ipft_tx.c misc.c ip_frag.c ip_state.c ip_nat.c ip_proxy.c \
+ ip_log.c ip_auth.c ipft_hx.c ip_fil.c natparse.c facpri.c common.c \
+ printstate.c printnat.c
+
+CFLAGS+= -DUSE_INET6 -DIPL_NAME=\"/dev/ipl\" -DIPFILTER_LOG
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter
+CFLAGS+= -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ipresend/Makefile b/usr.sbin/ipresend/Makefile
new file mode 100644
index 0000000..f1b7036
--- /dev/null
+++ b/usr.sbin/ipresend/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+MAINTAINER= darrenr@freebsd.org
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= ipresend
+SRCS= ipresend.c ip.c resend.c opt.c ipft_ef.c ipft_hx.c ipft_sn.c ipft_td.c \
+ ipft_tx.c sbpf.c 44arp.c ipft_pc.c
+
+CFLAGS+= -DDOSOCKET -DIPL_NAME=\"/dev/ipl\" -DUSE_INET6
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I${.CURDIR}/../../contrib/ipfilter/ipsend \
+ -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ipsend/Makefile b/usr.sbin/ipsend/Makefile
new file mode 100644
index 0000000..8ba53cc
--- /dev/null
+++ b/usr.sbin/ipsend/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+MAINTAINER= darrenr@freebsd.org
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter/iplang \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= ipsend
+MAN= ipsend.1 ipsend.5
+SRCS= ipsend.c ip.c ipsopt.c sbpf.c sock.c 44arp.c iplang_y.y iplang_l.l \
+ y.tab.h
+
+CFLAGS+= -DDOSOCKET -DIPL_NAME=\"/dev/ipl\" -DUSE_INET6
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I. -I${.CURDIR}/../../contrib/ipfilter/ipsend \
+ -I${.CURDIR}/../../contrib/ipfilter/iplang \
+ -I${.CURDIR}/../../contrib/ipfilter
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/iptest/Makefile b/usr.sbin/iptest/Makefile
new file mode 100644
index 0000000..eb61d2d
--- /dev/null
+++ b/usr.sbin/iptest/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+MAINTAINER= darrenr@freebsd.org
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= iptest
+SRCS= iptest.c iptests.c ip.c sbpf.c 44arp.c sock.c
+
+CFLAGS+= -DDOSOCKET -DIPL_NAME=\"/dev/ipl\" -DUSE_INET6
+CFLAGS+= -I${.CURDIR}/../../sys/contrib/ipfilter/netinet
+CFLAGS+= -I${.CURDIR}/../../contrib/ipfilter/ipsend \
+ -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jail/Makefile b/usr.sbin/jail/Makefile
new file mode 100644
index 0000000..7181928
--- /dev/null
+++ b/usr.sbin/jail/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= jail
+MAN= jail.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8
new file mode 100644
index 0000000..6e159a1
--- /dev/null
+++ b/usr.sbin/jail/jail.8
@@ -0,0 +1,546 @@
+.\"
+.\" 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 April 8, 2003
+.Dt JAIL 8
+.Os
+.Sh NAME
+.Nm jail
+.Nd "imprison process and its descendants"
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Op Fl u Ar username | Fl U Ar username
+.Ar path hostname ip-number 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 i
+Output the jail identifier of 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-number
+IP number assigned to the prison.
+.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"
+This example shows how to set up a jail directory tree
+containing an entire
+.Fx
+distribution:
+.Bd -literal
+D=/here/is/the/jail
+cd /usr/src
+mkdir -p $D
+make world DESTDIR=$D
+cd etc
+make distribution DESTDIR=$D
+mount_devfs devfs $D/dev
+cd $D
+ln -sf dev/null kernel
+.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.
+.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.168.11.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.
+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.168.11.23"
+rpcbind_enable="NO"
+.Ed
+.Pp
+.Li 192.168.11.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.168.11.100 testhostname 192.168.11.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
+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
+Configure
+.Pa /etc/resolv.conf
+so that name resolution within the jail will work correctly
+.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.168.11.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.168.11.100/32
+mount -t procfs proc /data/jail/192.168.11.100/proc
+jail /data/jail/192.168.11.100 testhostname 192.168.11.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.
+.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 conjuction with the one of the
+.Xr kill 1
+commands above, or use the
+.Xr killall 1
+utility with the
+.Fl j
+option.
+.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
+.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.getfsstatroot_only
+This MIB entry determines whether or not processes within a jail are able
+to see data for all mountpoints.
+When set to 1 (default), the
+.Xr getfsstat 2
+system call returns only (when called by jailed processes) the data for
+the file system on which the jail's root vnode is located.
+Note: this also has the effect of hiding other mounts inside a jail,
+such as
+.Pa /dev ,
+.Pa /tmp ,
+and
+.Pa /proc ,
+but errs on the side of leaking less information.
+.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.
+.El
+.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 newaliases 1 ,
+.Xr ps 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_devfs 8 ,
+.Xr named 8 ,
+.Xr reboot 8 ,
+.Xr rpcbind 8 ,
+.Xr sendmail 8 ,
+.Xr shutdown 8 ,
+.Xr sysctl 8 ,
+.Xr syslogd 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.
+.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..1e5ff9b
--- /dev/null
+++ b/usr.sbin/jail/jail.c
@@ -0,0 +1,126 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <login_cap.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void);
+
+#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;
+ struct jail j;
+ struct passwd *pwd;
+ struct in_addr in;
+ int ch, groups[NGROUPS], i, iflag, ngroups, uflag, Uflag;
+ char *username;
+
+ iflag = uflag = Uflag = 0;
+ username = NULL;
+
+ while ((ch = getopt(argc, argv, "iu:U:")) != -1) {
+ switch (ch) {
+ case 'i':
+ iflag = 1;
+ break;
+ case 'u':
+ username = optarg;
+ uflag = 1;
+ break;
+ case 'U':
+ username = optarg;
+ Uflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 4)
+ usage();
+ if (uflag && Uflag)
+ usage();
+ if (uflag)
+ GET_USER_INFO;
+ if (chdir(argv[0]) != 0)
+ err(1, "chdir: %s", argv[0]);
+ memset(&j, 0, sizeof(j));
+ j.version = 0;
+ j.path = argv[0];
+ j.hostname = argv[1];
+ if (inet_aton(argv[2], &in) == 0)
+ errx(1, "Could not make sense of ip-number: %s", argv[2]);
+ j.ip_number = ntohl(in.s_addr);
+ i = jail(&j);
+ if (i == -1)
+ err(1, "jail");
+ if (iflag) {
+ printf("%d\n", i);
+ fflush(stdout);
+ }
+ 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) != 0)
+ err(1, "setusercontext");
+ login_close(lcap);
+ }
+ if (execv(argv[3], argv + 3) != 0)
+ err(1, "execv: %s", argv[3]);
+ exit(0);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "%s%s\n",
+ "usage: jail [-i] [-u username | -U username]",
+ " path hostname ip-number command ...");
+ exit(1);
+}
diff --git a/usr.sbin/jexec/Makefile b/usr.sbin/jexec/Makefile
new file mode 100644
index 0000000..87e9926
--- /dev/null
+++ b/usr.sbin/jexec/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= jexec
+MAN= jexec.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jexec/jexec.8 b/usr.sbin/jexec/jexec.8
new file mode 100644
index 0000000..a5749c1
--- /dev/null
+++ b/usr.sbin/jexec/jexec.8
@@ -0,0 +1,52 @@
+.\"
+.\" 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 April 8, 2003
+.Dt JEXEC 8
+.Os
+.Sh NAME
+.Nm jexec
+.Nd "execute a command inside an existing jail"
+.Sh SYNOPSIS
+.Nm
+.Ar jid command ...
+.Sh DESCRIPTION
+The
+.Nm
+utility executes
+.Ar command
+inside the jail identified by
+.Ar jid .
+.Sh SEE ALSO
+.Xr jail_attach 2 ,
+.Xr jail 8 ,
+.Xr jls 8
+.Sh HISTORY
+The
+.Nm
+utility was added in
+.Fx 5.1 .
diff --git a/usr.sbin/jexec/jexec.c b/usr.sbin/jexec/jexec.c
new file mode 100644
index 0000000..089a896
--- /dev/null
+++ b/usr.sbin/jexec/jexec.c
@@ -0,0 +1,62 @@
+/*-
+ * 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$
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int jid;
+
+ if (argc < 3)
+ usage();
+ jid = (int)strtol(argv[1], NULL, 10);
+ if (jail_attach(jid) == -1)
+ err(1, "jail_attach(): %d", jid);
+ if (chdir("/") == -1)
+ err(1, "chdir(): /");
+ if (execvp(argv[2], argv + 2) == -1)
+ err(1, "execvp(): %s", argv[2]);
+ exit(0);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: jexec jid command [...]\n");
+ exit(1);
+}
diff --git a/usr.sbin/jls/Makefile b/usr.sbin/jls/Makefile
new file mode 100644
index 0000000..3968946
--- /dev/null
+++ b/usr.sbin/jls/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= jls
+MAN= jls.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jls/jls.8 b/usr.sbin/jls/jls.8
new file mode 100644
index 0000000..4dc68c7
--- /dev/null
+++ b/usr.sbin/jls/jls.8
@@ -0,0 +1,50 @@
+.\"
+.\" 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 April 8, 2003
+.Dt JLS 8
+.Os
+.Sh NAME
+.Nm jls
+.Nd "list active jails"
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility lists all active jails.
+Each jail is represented by one row which contains the following columns:
+jail identifier (JID), IP address, hostname, and path.
+.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..0ad36d1
--- /dev/null
+++ b/usr.sbin/jls/jls.c
@@ -0,0 +1,77 @@
+/*-
+ * 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$
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/sysctl.h>
+
+#include <arpa/inet.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int
+main(void)
+{
+ struct xprison *sxp, *xp;
+ struct in_addr in;
+ size_t i, len;
+
+ if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+ err(1, "sysctlbyname(): security.jail.list");
+retry:
+ if (len <= 0)
+ exit(0);
+ sxp = xp = calloc(len, 1);
+ if (sxp == NULL)
+ err(1, "malloc()");
+
+ if (sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) {
+ if (errno == ENOMEM) {
+ free(sxp);
+ goto retry;
+ }
+ err(1, "sysctlbyname(): security.jail.list");
+ }
+ if (len < sizeof(*xp) || len % sizeof(*xp) ||
+ xp->pr_version != XPRISON_VERSION)
+ errx(1, "Kernel and userland out of sync");
+
+ printf(" JID IP Address Hostname Path\n");
+ for (i = 0; i < len / sizeof(*xp); i++) {
+ in.s_addr = ntohl(xp->pr_ip);
+ printf("%6d %-15.15s %-29.29s %.74s\n",
+ xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path);
+ xp++;
+ }
+ free(sxp);
+ 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..8ef617f
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.1
@@ -0,0 +1,235 @@
+.\"
+.\" 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 May 27, 2001
+.Dt KBDCONTROL 1
+.Os
+.Sh NAME
+.Nm kbdcontrol
+.Nd keyboard control and configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl dFKix
+.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 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.
+.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 .
+.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/kbd1 < /dev/console
+.Pp
+To switch back to the default keyboard, use this command.
+.Pp
+.Dl kbdcontrol -k /dev/kbd0
+.Sh BUGS
+Report when found.
+.Sh SEE ALSO
+.Xr kbdmap 1 ,
+.Xr vidcontrol 1 ,
+.Xr atkbd 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
diff --git a/usr.sbin/kbdcontrol/kbdcontrol.c b/usr.sbin/kbdcontrol/kbdcontrol.c
new file mode 100644
index 0000000..0f2ad21
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.c
@@ -0,0 +1,1142 @@
+/*-
+ * 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 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;
+ }
+ strcpy(fkey.keydef, string);
+ 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 = 2;
+ opt += 6;
+ }
+ if (!strcmp(opt, "visual"))
+ bell |= 1;
+ 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 & ~2) == 0)
+ 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
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: kbdcontrol [-dFKix] [-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, "b:df:iKk:Fl:L:r:x")) != -1)
+ switch(opt) {
+ 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..bd3c179
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdmap.5
@@ -0,0 +1,313 @@
+.\" 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 September 11, 2000
+.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.
+.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.
+.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..38d0212
--- /dev/null
+++ b/usr.sbin/kbdmap/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= kbdmap
+LINKS= ${BINDIR}/kbdmap ${BINDIR}/vidfont
+MAN= kbdmap.1
+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..dd9f10b
--- /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 | Fl default
+.Op Fl h | Fl help
+.Op Fl l | Fl lang Ar language
+.Op Fl p | Fl print
+.Op Fl r | Fl restore
+.Op Fl s | Fl show
+.Op Fl v | Fl 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 , Fl default
+Use default language.
+Ignore
+.Ev LANG
+environment variable.
+.It Fl h , Fl help
+Print options and exit.
+.It Fl l , Fl lang Ar language
+Use
+.Ar language
+for description and menu
+.It Fl p , Fl print
+Print description of available keymaps or fonts
+to stdout and exit.
+.It Fl r , Fl restore
+Load default font from
+.Pa /etc/rc.conf .
+.It Fl s , Fl show
+Show currently supported languages and exit.
+.It Fl v , Fl verbose
+More warnings.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width LANG -compact
+.It Ev LANG
+preferred language
+.El
+.Sh FILES
+.Bl -tag -width /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 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 .
+.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 .
diff --git a/usr.sbin/kbdmap/kbdmap.c b/usr.sbin/kbdmap/kbdmap.c
new file mode 100644
index 0000000..5f2bffd
--- /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);
+
+ printf("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, 64, 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] = '.';
+
+ 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]);
+
+ /* Parse command line arguments */
+ parse_args(argc, argv);
+
+ font_current = get_font();
+ if (font_current == NULL)
+ font_current = font_default;
+
+ if (strcmp(program, "kbdmap"))
+ dir = fontdir;
+ else
+ dir = keymapdir;
+
+ /* 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..7109d7a
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.8
@@ -0,0 +1,86 @@
+.\" 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.
+.\"
+.\" $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..ea0a3d8
--- /dev/null
+++ b/usr.sbin/keyserv/Makefile
@@ -0,0 +1,25 @@
+# $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.
+
+DISTRIBUTION= crypto
+DPADD= ${LIBMP} ${LIBCRYPTO} ${LIBRPCSVC}
+LDADD= -lmp -lcrypto -lrpcsvc
+
+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..9a907a7
--- /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 __P(( des_block * ));
+static void usage __P(( void ));
+static int getrootkey __P(( des_block *, int ));
+static int root_auth __P(( 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 __P(( uid_t, cryptkeyarg2 * ));
+cryptkeyres *key_decrypt_pk_2_svc_prog __P(( uid_t, cryptkeyarg2 * ));
+des_block *key_gen_1_svc_prog __P(( 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..3395f28
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.h
@@ -0,0 +1,20 @@
+/*
+ * $FreeBSD$
+ */
+extern void setmodulus __P((char *modx));
+
+extern keystatus pk_setkey __P(( uid_t, keybuf ));
+extern keystatus pk_encrypt __P(( uid_t, char *, netobj *, des_block * ));
+extern keystatus pk_decrypt __P(( uid_t, char *, netobj *, des_block * ));
+extern keystatus pk_netput __P(( uid_t, key_netstarg * ));
+extern keystatus pk_netget __P(( uid_t, key_netstarg * ));
+extern keystatus pk_get_conv_key __P(( uid_t, keybuf, cryptkeyres * ));
+extern void pk_nodefaultkeys __P(( void ));
+
+extern void crypt_prog_1 __P(( struct svc_req *, register SVCXPRT * ));
+extern void load_des __P(( int, char * ));
+
+extern int (*_my_crypt)__P(( char *, int, struct desparams * ));
+
+extern char ROOTKEY[];
+
diff --git a/usr.sbin/keyserv/setkey.c b/usr.sbin/keyserv/setkey.c
new file mode 100644
index 0000000..566b1c7
--- /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 __P(( uid_t ));
+static void writecache __P(( char *, char *, des_block * ));
+static int readcache __P(( char *, char *, des_block * ));
+static void extractdeskey __P (( MINT *, des_block * ));
+static int storesecretkey __P(( uid_t, keybuf ));
+static keystatus pk_crypt __P(( 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 = 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 __P(( uid_t, key_netstarg * ));
+static int fetch_netname __P(( 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 = xtom(xpublic);
+ secret = xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = itom(0);
+ pow(public, secret, MODULUS, common);
+ extractdeskey(common, &deskey);
+ writecache(xpublic, xsecret, &deskey);
+ mfree(secret);
+ mfree(public);
+ 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 = xtom(xpublic);
+ secret = xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = itom(0);
+ pow(public, secret, MODULUS, common);
+ extractdeskey(common, &result->cryptkeyres_u.deskey);
+ writecache(xpublic, xsecret, &result->cryptkeyres_u.deskey);
+ mfree(secret);
+ mfree(public);
+ 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 = itom(0);
+#ifdef SOLARIS_MP
+ _mp_move(ck, a);
+#else
+ move(ck, a);
+#endif
+ for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
+ sdiv(a, base, a, &r);
+ }
+ k = deskey->c;
+ for (i = 0; i < 8; i++) {
+ sdiv(a, base, a, &r);
+ *k++ = r;
+ }
+ 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..964c79b
--- /dev/null
+++ b/usr.sbin/kgmon/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= kgmon
+MAN= kgmon.8
+
+# 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..41628d6
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.8
@@ -0,0 +1,133 @@
+.\" 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.
+.\"
+.\" @(#)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 SEE ALSO
+.Xr gprof 1 ,
+.Xr config 8
+.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 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..50651bc
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.c
@@ -0,0 +1,538 @@
+/*
+ * 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.
+ * 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, 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 ncessary 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 %u, got %d: %s",
+ kvp->gpm.kcountsize, 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 %u, got %d: %s",
+ kvp->gpm.fromssize, 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 %u, got %d: %s",
+ kvp->gpm.tossize, i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if (debug)
+ warnx("lowpc 0x%x, textsize 0x%x",
+ 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%x selfpc 0x%x count %d",
+ 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..50bb0b7
--- /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 <stddef.h>
+#include "elfhdr.h"
+#include "endian.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/endian.h b/usr.sbin/kgzip/endian.h
new file mode 100644
index 0000000..78c9b16
--- /dev/null
+++ b/usr.sbin/kgzip/endian.h
@@ -0,0 +1,52 @@
+/*
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <machine/endian.h>
+
+#define bswap16(x) (uint16_t) \
+ ((x >> 8) | (x << 8))
+
+#define bswap32(x) (uint32_t) \
+ ((x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24))
+
+#define bswap64(x) (uint64_t) \
+ ((x >> 56) | ((x >> 40) & 0xff00) | ((x >> 24) & 0xff0000) | \
+ ((x >> 8) & 0xff000000) | ((x << 8) & ((uint64_t)0xff << 32)) | \
+ ((x << 24) & ((uint64_t)0xff << 40)) | \
+ ((x << 40) & ((uint64_t)0xff << 48)) | ((x << 56)))
+
+/*
+ * Host to big endian, host to little endian, big endian to host, and little
+ * endian to host byte order functions as detailed in byteorder(9).
+ */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+#define HTOBE16(x) bswap16((uint16_t)(x))
+#define HTOBE32(x) bswap32((uint32_t)(x))
+#define HTOBE64(x) bswap64((uint64_t)(x))
+#define HTOLE16(x) ((uint16_t)(x))
+#define HTOLE32(x) ((uint32_t)(x))
+#define HTOLE64(x) ((uint64_t)(x))
+
+#define BE16TOH(x) bswap16((uint16_t)(x))
+#define BE32TOH(x) bswap32((uint32_t)(x))
+#define BE64TOH(x) bswap64((uint64_t)(x))
+#define LE16TOH(x) ((uint16_t)(x))
+#define LE32TOH(x) ((uint32_t)(x))
+#define LE64TOH(x) ((uint64_t)(x))
+#else /* _BYTE_ORDER != _LITTLE_ENDIAN */
+#define HTOBE16(x) ((uint16_t)(x))
+#define HTOBE32(x) ((uint32_t)(x))
+#define HTOBE64(x) ((uint64_t)(x))
+#define HTOLE16(x) bswap16((uint16_t)(x))
+#define HTOLE32(x) bswap32((uint32_t)(x))
+#define HTOLE64(x) bswap64((uint64_t)(x))
+
+#define BE16TOH(x) ((uint16_t)(x))
+#define BE32TOH(x) ((uint32_t)(x))
+#define BE64TOH(x) ((uint64_t)(x))
+#define LE16TOH(x) bswap16((uint16_t)(x))
+#define LE32TOH(x) bswap32((uint32_t)(x))
+#define LE64TOH(x) bswap64((uint64_t)(x))
+#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */
diff --git a/usr.sbin/kgzip/i386_a.out.h b/usr.sbin/kgzip/i386_a.out.h
new file mode 100644
index 0000000..72d930d
--- /dev/null
+++ b/usr.sbin/kgzip/i386_a.out.h
@@ -0,0 +1,139 @@
+/*-
+ * 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.
+ * 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: @(#)exec.h 8.1 (Berkeley) 6/11/93
+ * from: FreeBSD: src/sys/sys/imgact_aout.h,v 1.18 2002/09/05 07:54:03 bde Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _I386_AOUT_H_
+#define _I386_AOUT_H_
+
+#include <sys/types.h>
+#include "endian.h"
+
+#define __I386_LDPGSZ 4096
+
+#define I386_N_GETMAGIC(ex) \
+ ( LE32TOH((ex).a_midmag) & 0xffff )
+#define I386_N_SETMAGIC(ex,mag,mid,flag) \
+ ( (ex).a_midmag = HTOLE32((((flag) & 0x3f) <<26) | \
+ (((mid) & 0x03ff) << 16) | \
+ ((mag) & 0xffff)) )
+
+#define I386_N_ALIGN(ex,x) \
+ (I386_N_GETMAGIC(ex) == ZMAGIC || I386_N_GETMAGIC(ex) == QMAGIC ? \
+ ((x) + __I386_LDPGSZ - 1) & ~(uint32_t)(__I386_LDPGSZ - 1) : (x))
+
+/* Valid magic number check. */
+#define I386_N_BADMAG(ex) \
+ (I386_N_GETMAGIC(ex) != OMAGIC && I386_N_GETMAGIC(ex) != NMAGIC && \
+ I386_N_GETMAGIC(ex) != ZMAGIC && I386_N_GETMAGIC(ex) != QMAGIC)
+
+
+/* Address of the bottom of the text segment. */
+/*
+ * This can not be done right. Abuse a_entry in some cases to handle kernels.
+ */
+#define I386_N_TXTADDR(ex) \
+ ((I386_N_GETMAGIC(ex) == OMAGIC || I386_N_GETMAGIC(ex) == NMAGIC || \
+ I386_N_GETMAGIC(ex) == ZMAGIC) ? \
+ (LE32TOH((ex).a_entry) < LE32TOH((ex).a_text) ? 0 : \
+ LE32TOH((ex).a_entry) & ~__I386_LDPGSZ) : __I386_LDPGSZ)
+
+/* Address of the bottom of the data segment. */
+#define I386_N_DATADDR(ex) \
+ I386_N_ALIGN(ex, I386_N_TXTADDR(ex) + LE32TOH((ex).a_text))
+
+/* Text segment offset. */
+#define I386_N_TXTOFF(ex) \
+ (I386_N_GETMAGIC(ex) == ZMAGIC ? __I386_LDPGSZ : \
+ (I386_N_GETMAGIC(ex) == QMAGIC ? 0 : sizeof(struct exec)))
+
+/* Data segment offset. */
+#define I386_N_DATOFF(ex) \
+ I386_N_ALIGN(ex, I386_N_TXTOFF(ex) + LE32TOH((ex).a_text))
+
+/* Relocation table offset. */
+#define I386_N_RELOFF(ex) \
+ I386_N_ALIGN(ex, I386_N_DATOFF(ex) + LE32TOH((ex).a_data))
+
+/* Symbol table offset. */
+#define I386_N_SYMOFF(ex) \
+ (I386_N_RELOFF(ex) + LE32TOH((ex).a_trsize) + LE32TOH((ex).a_drsize))
+
+/* String table offset. */
+#define I386_N_STROFF(ex) (I386_N_SYMOFF(ex) + LE32TOH((ex).a_syms))
+
+/*
+ * Header prepended to each a.out file.
+ * only manipulate the a_midmag field via the
+ * N_SETMAGIC/N_GET{MAGIC,MID,FLAG} macros in a.out.h
+ */
+
+struct i386_exec {
+ uint32_t a_midmag; /* flags<<26 | mid<<16 | magic */
+ uint32_t a_text; /* text segment size */
+ uint32_t a_data; /* initialized data size */
+ uint32_t a_bss; /* uninitialized data size */
+ uint32_t a_syms; /* symbol table size */
+ uint32_t a_entry; /* entry point */
+ uint32_t a_trsize; /* text relocation size */
+ uint32_t a_drsize; /* data relocation size */
+};
+#define a_magic a_midmag /* XXX Hack to work with current kern_execve.c */
+
+/* a_magic */
+#define OMAGIC 0407 /* old impure format */
+#define NMAGIC 0410 /* read-only text */
+#define ZMAGIC 0413 /* demand load format */
+#define QMAGIC 0314 /* "compact" demand load format */
+
+/* a_mid */
+#define MID_ZERO 0 /* unknown - implementation dependent */
+#define MID_SUN010 1 /* sun 68010/68020 binary */
+#define MID_SUN020 2 /* sun 68020-only binary */
+#define MID_I386 134 /* i386 BSD binary */
+#define MID_SPARC 138 /* sparc */
+#define MID_HP200 200 /* hp200 (68010) BSD binary */
+#define MID_HP300 300 /* hp300 (68020+68881) BSD binary */
+#define MID_HPUX 0x20C /* hp200/300 HP-UX binary */
+#define MID_HPUX800 0x20B /* hp800 HP-UX binary */
+
+/*
+ * a_flags
+ */
+#define EX_PIC 0x10 /* contains position independent code */
+#define EX_DYNAMIC 0x20 /* contains run-time link-edit info */
+#define EX_DPMASK 0x30 /* mask for the above */
+
+#endif /* !_I386_AOUT_H_ */
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..e873b11
--- /dev/null
+++ b/usr.sbin/kgzip/kgzcmp.c
@@ -0,0 +1,235 @@
+/*
+ * 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/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "i386_a.out.h"
+
+#include "aouthdr.h"
+#include "elfhdr.h"
+#include "endian.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 i386_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 i386_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) && I386_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 i386_exec * a)
+{
+ size_t load, addr;
+
+ load = addr = I386_N_TXTADDR(*a);
+ xcopy(idi, ido, LE32TOH(a->a_text), I386_N_TXTOFF(*a));
+ addr += LE32TOH(a->a_text);
+ if (I386_N_DATADDR(*a) != addr)
+ return -1;
+ xcopy(idi, ido, LE32TOH(a->a_data), I386_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..653a2ad
--- /dev/null
+++ b/usr.sbin/kgzip/kgzip.8
@@ -0,0 +1,141 @@
+.\" 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 SEE ALSO
+.Xr gzip 1 ,
+.Xr ld 1 ,
+.Xr a.out 5 ,
+.Xr elf 5 ,
+.Xr boot 8 ,
+.Xr loader 8
+.Sh DIAGNOSTICS
+.Ex -std
+.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..4f6acc8
--- /dev/null
+++ b/usr.sbin/kgzip/kgzld.c
@@ -0,0 +1,100 @@
+/*
+ * 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/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..c270085
--- /dev/null
+++ b/usr.sbin/kldxref/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+PROG= kldxref
+MAN= kldxref.8
+SRCS= kldxref.c ef.c
+
+WARNS?= 2
+
+.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..142c9cb
--- /dev/null
+++ b/usr.sbin/kldxref/ef.c
@@ -0,0 +1,505 @@
+/*
+ * 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"
+
+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);
+
+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(" ");
+ }
+}
+
+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;
+}
+
+/*
+ * 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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+}
+
+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);
+ 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);
+ return (ef_reloc(ef, offset, len, dest));
+}
+
+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;
+}
+
+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, elf_file_t ef, int verbose)
+{
+ Elf_Ehdr *hdr;
+ int fd;
+ int error;
+ int phlen, res;
+ int nsegs;
+ Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit;
+
+ bzero(ef, sizeof(*ef));
+ if (filename == NULL)
+ return EFTYPE;
+ ef->ef_verbose = verbose;
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return errno;
+ ef->ef_fd = fd;
+ ef->ef_name = strdup(filename);
+ 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);
+ if (ef->ef_verbose)
+ warnc(error, "elf_open(%s)", filename);
+ }
+ return error;
+}
+
+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);
+ return 0;
+}
diff --git a/usr.sbin/kldxref/ef.h b/usr.sbin/kldxref/ef.h
new file mode 100644
index 0000000..f9a7884
--- /dev/null
+++ b/usr.sbin/kldxref/ef.h
@@ -0,0 +1,50 @@
+/* $FreeBSD$ */
+
+#ifndef _EF_H_
+#define _EF_H_
+
+#define EFT_KLD 1
+#define EFT_KERNEL 2
+
+typedef struct elf_file {
+ char* ef_name;
+ 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 */
+} *elf_file_t;
+
+__BEGIN_DECLS
+int ef_open(const char *, elf_file_t, int);
+int ef_close(elf_file_t ef);
+int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
+int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr);
+int ef_reloc(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr);
+int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr);
+int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
+__END_DECLS
+
+#endif /* _EF_H_*/
diff --git a/usr.sbin/kldxref/ef_nop.c b/usr.sbin/kldxref/ef_nop.c
new file mode 100644
index 0000000..3fff5de
--- /dev/null
+++ b/usr.sbin/kldxref/ef_nop.c
@@ -0,0 +1,39 @@
+/*-
+ * 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(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
+{
+
+ return (0);
+}
diff --git a/usr.sbin/kldxref/ef_sparc64.c b/usr.sbin/kldxref/ef_sparc64.c
new file mode 100644
index 0000000..bc728a4
--- /dev/null
+++ b/usr.sbin/kldxref/ef_sparc64.c
@@ -0,0 +1,63 @@
+/*-
+ * 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.
+ */
+int
+ef_reloc(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
+{
+ const Elf_Rela *a;
+ Elf_Word w;
+
+ for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
+ if (a->r_offset >= offset && a->r_offset < offset + len) {
+ switch (ELF_R_TYPE(a->r_info)) {
+ case R_SPARC_RELATIVE:
+ /* load address is 0 */
+ w = a->r_addend;
+ memcpy((u_char *)dest + (a->r_offset - offset),
+ &w, sizeof(w));
+ break;
+ default:
+ warnx("unhandled relocation type %u",
+ ELF_R_TYPE(a->r_info));
+ break;
+ }
+ }
+ }
+ return (0);
+}
diff --git a/usr.sbin/kldxref/fileformat b/usr.sbin/kldxref/fileformat
new file mode 100644
index 0000000..2eddcca
--- /dev/null
+++ b/usr.sbin/kldxref/fileformat
@@ -0,0 +1,40 @@
+$FreeBSD$
+
+ linker.hints file consists from the one or more records. 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 MTD_* values */
+};
+
+ The rest of record depends on the type.
+
+struct string {
+ int length; /* length of string */
+ char val[]; /* string itself (no terminating zero) */
+};
+
+struct data_mdt_version {
+ int type = MDT_VERSION;
+ struct string modname;
+ int version;
+ struct string kldname;
+};
+
+struct data_mdt_module {
+ int type = MDT_VERSION;
+ struct string modname;
+ struct string kldname;
+};
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..0597cce
--- /dev/null
+++ b/usr.sbin/kldxref/kldxref.c
@@ -0,0 +1,356 @@
+/*
+ * 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
+
+#ifndef min
+#define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+struct mod_info {
+ char* mi_name;
+ int mi_ver;
+ SLIST_ENTRY(mod_info) mi_next;
+};
+
+#ifdef notnow
+struct kld_info {
+ char* k_filename;
+ SLIST_HEAD(mod_list_head, mod_info) k_modules;
+ SLIST_ENTRY(kld_info) k_next;
+};
+
+SLIST_HEAD(kld_list_head, kld_info) kldlist;
+#endif
+
+static int dflag, verbose;
+
+FILE *fxref;
+
+static const char *xref_file = "linker.hints";
+
+static char recbuf[MAXRECSIZE];
+static int recpos, reccnt;
+
+void maketempfile(char *, const char *);
+static void usage(void);
+
+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 (dflag || 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;
+}
+
+static int
+record_int(int val)
+{
+ intalign();
+ return record_buf(&val, sizeof(val));
+}
+
+static int
+record_byte(u_char val)
+{
+ return record_buf(&val, sizeof(val));
+}
+
+static int
+record_string(const char *str)
+{
+ int len = strlen(str);
+ int error;
+
+ if (dflag)
+ return 0;
+ error = record_byte(len);
+ 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));
+ record_int(MDT_VERSION);
+ record_string(cval);
+ record_int(mdv.mv_version);
+ record_string(kldname);
+ if (!dflag)
+ break;
+ printf(" interface %s.%d\n", cval, mdv.mv_version);
+ break;
+ case MDT_MODULE:
+ record_int(MDT_MODULE);
+ record_string(cval);
+ record_string(kldname);
+ if (!dflag)
+ break;
+ printf(" module %s\n", cval);
+ break;
+ default:
+ warnx("unknown metdata 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;
+/* struct kld_info *kip;
+ struct mod_info *mip;*/
+ void **p, **orgp;
+ int error, nmlen;
+ long start, finish, entries;
+ Elf_Sym *sym;
+ char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp;
+
+ if (verbose || dflag)
+ printf("%s\n", filename);
+ error = ef_open(filename, &ef, verbose);
+ if (error)
+ return error;
+ if (ef.ef_type != EFT_KLD && ef.ef_type != EFT_KERNEL) {
+ ef_close(&ef);
+ return 0;
+ }
+ if (!dflag) {
+ cp = strrchr(kldname, '.');
+ nmlen = cp ? min(MAXMODNAME, cp - kldname) :
+ min(MAXMODNAME, strlen(kldname));
+ strncpy(kldmodname, kldname, nmlen);
+ kldmodname[nmlen] = '\0';
+/* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/
+ }
+ do {
+ check(ef_lookup_symbol(&ef, "__start_set_" MDT_SETNAME, &sym));
+ start = sym->st_value;
+ check(ef_lookup_symbol(&ef, "__stop_set_" MDT_SETNAME, &sym));
+ finish = sym->st_value;
+ entries = (finish - start) / sizeof(void *);
+ 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;
+}
+
+void
+maketempfile(char *dest, const char *root)
+{
+ char *p;
+
+ strncpy(dest, root, MAXPATHLEN - 1);
+ dest[MAXPATHLEN] = '\0';
+
+ if ((p = strrchr(dest, '/')) != 0)
+ p++;
+ else
+ p = dest;
+ strcpy(p, "lhint.XXXXXX");
+ if (mkstemp(dest) == -1)
+ err(1, "%s", dest);
+}
+
+static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
+
+int
+main(int argc, char *argv[])
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int opt, fts_options, ival;
+ struct stat sb;
+
+ fts_options = FTS_PHYSICAL;
+/* SLIST_INIT(&kldlist);*/
+
+ while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
+ switch (opt) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ xref_file = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'R':
+ 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) && !dflag && fxref) {
+ fclose(fxref);
+ if (reccnt) {
+ rename(tempname, xrefname);
+ } else {
+ unlink(tempname);
+ unlink(xrefname);
+ }
+ }
+ if (p == NULL)
+ break;
+ if (p && p->fts_info == FTS_D && !dflag) {
+ snprintf(xrefname, sizeof(xrefname), "%s/%s",
+ ftsp->fts_path, xref_file);
+ maketempfile(tempname, ftsp->fts_path);
+ fxref = fopen(tempname, "w+t");
+ if (fxref == NULL)
+ err(1, "can't create %s", tempname);
+ ival = 1;
+ fwrite(&ival, sizeof(ival), 1, fxref);
+ reccnt = 0;
+ }
+ if (p->fts_info != FTS_F)
+ continue;
+ read_kld(p->fts_path, p->fts_name);
+ }
+ fts_close(ftsp);
+ return 0;
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n",
+ "usage: kldxref [-Rdv] [-f hintsfile] path ..."
+ );
+ exit(1);
+}
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..ce401ed
--- /dev/null
+++ b/usr.sbin/lastlogin/lastlogin.8
@@ -0,0 +1,78 @@
+.\" $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/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..13b94c2
--- /dev/null
+++ b/usr.sbin/lpr/Makefile.inc
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+CFLAGS+= -DINET6
+
+.if exists(${.OBJDIR}/../common_source)
+LIBLPR= ${.OBJDIR}/../common_source/liblpr.a
+.else
+LIBLPR= ${.CURDIR}/../common_source/liblpr.a
+.endif
+
+.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..faeb266
--- /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= yes
+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..e026662
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/chkprintcap.8
@@ -0,0 +1,96 @@
+.\" 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..352f79c
--- /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= YES
+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..d2ffb6a
--- /dev/null
+++ b/usr.sbin/lpr/common_source/common.c
@@ -0,0 +1,716 @@
+/*
+ * 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 <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);
+
+/*
+ * 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));
+}
+
+/* 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..67cac6b
--- /dev/null
+++ b/usr.sbin/lpr/common_source/ctlinfo.c
@@ -0,0 +1,875 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * 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 <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;
+};
+
+#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':
+ /* don't allow userid's with a leading minus, either */
+ if (*lbuff == '-')
+ break;
+ if (*lbuff == '\0')
+ break;
+ 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, 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 {
+ for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) {
+ if (*cp <= ' ')
+ *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..cdac70c
--- /dev/null
+++ b/usr.sbin/lpr/common_source/displayq.c
@@ -0,0 +1,539 @@
+/*
+ * 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)
+{
+ register int copycnt;
+ 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';
+ 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, atoi(cf+3));
+ 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)
+{
+ register int *r, n;
+ register char **u, *cp;
+
+ 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
+ */
+ for (n = 0, cp = cfile+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == n && !strcmp(cp, 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..fea03b9
--- /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);
+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);
+int isowner(char *_owner, char *_file);
+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..eeeb0d4
--- /dev/null
+++ b/usr.sbin/lpr/common_source/matchjobs.c
@@ -0,0 +1,583 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * 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;
+ char *cp, *cf_numstr, *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);
+
+ /*
+ * 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.
+ *
+ * XXX - All this nonsense should really be handled in a single
+ * place, like getq()...
+ */
+ cf_numstr = jq->job_cfname + 3;
+ if (!isdigitch(*cf_numstr))
+ cf_numstr++;
+ jnum = 0;
+ for (cp = cf_numstr; (cp < cf_numstr + 5) && isdigitch(*cp); cp++)
+ jnum = jnum * 10 + (*cp - '0');
+ cf_hoststr = cp;
+ 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..3ae3e90
--- /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 | NI_WITHSCOPEID) != 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 | NI_WITHSCOPEID) != 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..589117a
--- /dev/null
+++ b/usr.sbin/lpr/common_source/rmjob.c
@@ -0,0 +1,396 @@
+/*
+ * 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);
+
+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)
+{
+ register int *r, n;
+ register char **u, *cp;
+ FILE *cfp;
+
+ /*
+ * Check for valid cf file name (mostly checking current).
+ */
+ if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
+ return(0);
+
+ if (all && (from_host == local_host || !strcmp(from_host, file+6)))
+ 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));
+ /*
+ * Check the request list
+ */
+ for (n = 0, cp = file+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == n && isowner(line+1, file))
+ 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))
+ 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.
+ */
+int
+isowner(char *owner, char *file)
+{
+ if (!strcmp(person, root) && (from_host == local_host ||
+ !strcmp(from_host, file+6)))
+ return (1);
+ if (!strcmp(person, owner) && !strcmp(from_host, file+6))
+ 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..6895b79
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2855/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= koi2855
+NOMAN= #true
+
+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..1e4e30d
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2alt/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= koi2alt
+NOMAN= #true
+
+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..100022d
--- /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
+NOMAN= noman
+
+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..0269971
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.1
@@ -0,0 +1,117 @@
+.\"
+.\" 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..2662ef1
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.8
@@ -0,0 +1,312 @@
+.\" 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 doesn't 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 SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr printcap 5 ,
+.Xr chkprintcap 8 ,
+.Xr lpd 8
+.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 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..e58911c
--- /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_EVENT, 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_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..0ecdde6
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.8
@@ -0,0 +1,321 @@
+.\" 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 06, 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 won't 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..e02839a
--- /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 __P((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 | NI_WITHSCOPEID);
+ 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 | NI_WITHSCOPEID);
+ 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 | NI_WITHSCOPEID);
+ 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..9d85f1f3
--- /dev/null
+++ b/usr.sbin/lpr/lpd/printjob.c
@@ -0,0 +1,1897 @@
+/*
+ * 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);
+
+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, 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);
+ }
+ if (stat(pp->lock_file, &stb) == 0 && (stb.st_mode & LFM_PRINT_DIS))
+ exit(0); /* printing disabled */
+ 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);
+ }
+ /* 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;
+
+ 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;
+
+ 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);
+}
+
+/*
+ * 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(0);
+ 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..ac4f9ee
--- /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)
+{
+ int 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, 80, 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..170e9e2
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.1
@@ -0,0 +1,140 @@
+.\" 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 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.
+.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.
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..2418485
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.1
@@ -0,0 +1,318 @@
+.\" 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 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 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 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..dd3750a
--- /dev/null
+++ b/usr.sbin/lpr/lpr/printcap.5
@@ -0,0 +1,426 @@
+.\" 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..04cfbe0
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.1
@@ -0,0 +1,149 @@
+.\" 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 SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lpd 8
+.Sh DIAGNOSTICS
+``Permission denied" if the user tries to remove files other than his
+own.
+.Sh BUGS
+Since there are race conditions possible in the update of the lock file,
+the currently active job may be incorrectly identified.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
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..c73b796
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.c
@@ -0,0 +1,86 @@
+/*
+ * 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>
+
+/*
+ * 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++) {
+ putchar(nc);
+ if (++nc == 0177)
+ nc = ' ';
+ }
+ putchar('\n');
+ }
+ (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..2d7bfe7
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.8
@@ -0,0 +1,108 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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 BUGS
+The relationship between the computed price and reality is
+as yet unknown.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
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..76f4f2d
--- /dev/null
+++ b/usr.sbin/lptcontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= lptcontrol
+MAN= lptcontrol.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lptcontrol/lptcontrol.8 b/usr.sbin/lptcontrol/lptcontrol.8
new file mode 100644
index 0000000..65d848b
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.8
@@ -0,0 +1,83 @@
+.\"
+.\" 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 September 3, 1994
+.Dt LPTCONTROL 8
+.Os
+.Sh NAME
+.Nm lptcontrol
+.Nd a utility for manipulating the lpt printer driver
+.Sh SYNOPSIS
+.Nm
+.Fl i | p | e | s
+.Op Fl d Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to set either the interrupt-driven, extended 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 i
+Turn on interrupt-driven mode.
+.It Fl p
+Turn on polled mode.
+.It Fl e
+Turn on extended mode.
+.It Fl s
+Turn on standard mode, i.e. turn off extended mode.
+.It Fl d Ar device
+Set the mode of the printer device specified by
+.Ar device .
+The default value for
+.Ar device
+is
+.Pa /dev/lpt0 .
+.El
+.Pp
+One of
+.Fl i , p
+or
+.Fl e
+must be specified.
+.Sh FILES
+.Bl -tag -width /sys/i386/conf/GENERIC -compact
+.It Pa /dev/lpt?
+printer devices
+.It Pa /dev/lpctl?
+printer control devices
+.It Pa /sys/i386/conf/GENERIC
+kernel configuration file
+.El
+.Sh BUGS
+Sure to be some.
+.Sh SEE ALSO
+.Xr lpt 4
+.Sh AUTHORS
+.An Geoffrey M. Rehmet
+.Sh HISTORY
+The
+.Nm
+utility
+first appeared in
+.Fx 1.1.5
diff --git a/usr.sbin/lptcontrol/lptcontrol.c b/usr.sbin/lptcontrol/lptcontrol.c
new file mode 100644
index 0000000..5fb9798
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.c
@@ -0,0 +1,95 @@
+/*
+ * 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 <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <dev/ppbus/lptio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+
+#define PATH_LPCTL _PATH_DEV "lpctl"
+#define DEFAULT_DEVICE _PATH_DEV "lpt0"
+#define IRQ_INVALID -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 -i | -p | -s | -e [-d device]\n");
+ exit(1);
+}
+
+static void set_interrupt_status(int irq_status, const char * file)
+{
+ int fd;
+
+ if((fd = open(file, O_WRONLY, 0660)) < 0)
+ err(1, "open");
+ if(ioctl(fd, LPT_IRQ, &irq_status) < 0)
+ err(1, "ioctl");
+ close(fd);
+}
+
+int main (int argc, char * argv[])
+{
+ int opt;
+ int irq_status = IRQ_INVALID;
+ const char *device = DEFAULT_DEVICE;
+
+ while((opt = getopt(argc, argv, "ipesd:")) != -1)
+ switch(opt) {
+ case 'i': irq_status = USE_IRQ; break;
+ case 'p': irq_status = DO_POLL; break;
+ case 'e': irq_status = USE_EXT_MODE; break;
+ case 's': irq_status = USE_STD_MODE; break;
+ case 'd': device = optarg; break;
+ default : usage();
+ }
+ if(irq_status == IRQ_INVALID)
+ usage();
+
+ set_interrupt_status(irq_status, device);
+
+ exit(0);
+}
diff --git a/usr.sbin/mailstats/Makefile b/usr.sbin/mailstats/Makefile
new file mode 100644
index 0000000..eddfdd9
--- /dev/null
+++ b/usr.sbin/mailstats/Makefile
@@ -0,0 +1,43 @@
+# @(#)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
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+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..f67e775
--- /dev/null
+++ b/usr.sbin/mailwrapper/Makefile
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+.if !defined(NO_MAILWRAPPER)
+PROG= mailwrapper
+MAN= mailwrapper.8
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+.endif
+
+.if !defined(NO_MAILWRAPPER) || !defined(NO_SENDMAIL)
+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 defined(NO_MAILWRAPPER) && !defined(NO_SENDMAIL)
+SYMLINKS+= /usr/libexec/sendmail/sendmail ${BINDIR}/mailwrapper
+.endif
+.endif
+
+afterinstall:
+.if !defined(NO_MAILWRAPPER)
+.if !exists(${DESTDIR}/etc/mail/mailer.conf)
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 \
+ ${.CURDIR}/../../etc/mail/mailer.conf ${DESTDIR}/etc/mail
+.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..b45c018
--- /dev/null
+++ b/usr.sbin/mailwrapper/mailwrapper.8
@@ -0,0 +1,164 @@
+.\" $NetBSD: mailwrapper.8,v 1.6 1999/03/25 16:40:17 is 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 December 16, 1998
+.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 replacement programs that plug replace
+.Xr sendmail 8
+helps in installing alternative MTAs, it essentially makes the
+configuration of the system depend on hard 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 DIAGNOSTICS
+The
+.Nm
+utility will return an error value and print a diagnostic if its configuration
+file is missing or malformed, or does not contain a mapping for the
+name under which
+.Nm
+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
+Perry E. Metzger <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..c71bb30
--- /dev/null
+++ b/usr.sbin/mailwrapper/mailwrapper.c
@@ -0,0 +1,190 @@
+/* $OpenBSD: mailwrapper.c,v 1.6 1999/12/17 05:06:28 mickey Exp $ */
+/* $NetBSD: mailwrapper.c,v 1.3 1999/05/29 18:18:15 christos 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 <stdlib.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <syslog.h>
+#include <stdarg.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 *, int);
+static void freearg(struct arglist *, int);
+
+extern const char *__progname; /* from crt0.o */
+
+static void
+initarg(al)
+ struct arglist *al;
+{
+ al->argc = 0;
+ al->maxc = 10;
+ if ((al->argv = malloc(al->maxc * sizeof(char *))) == NULL)
+ err(1, NULL);
+}
+
+static void
+addarg(al, arg, copy)
+ struct arglist *al;
+ const char *arg;
+ int copy;
+{
+ char **argv2;
+
+ if (al->argc == al->maxc) {
+ al->maxc <<= 1;
+
+ if ((argv2 = realloc(al->argv,
+ al->maxc * sizeof(char *))) == NULL) {
+ if (al->argv)
+ free(al->argv);
+ al->argv = NULL;
+ err(1, NULL);
+ } else {
+ al->argv = argv2;
+ }
+ }
+ if (copy) {
+ if ((al->argv[al->argc++] = strdup(arg)) == NULL)
+ err(1, NULL);
+ } else
+ al->argv[al->argc++] = (char *)arg;
+}
+
+static void
+freearg(al, copy)
+ struct arglist *al;
+ int copy;
+{
+ size_t i;
+ if (copy)
+ for (i = 0; i < al->argc; i++)
+ free(al->argv[i]);
+ free(al->argv);
+}
+
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[];
+ char *envp[];
+{
+ FILE *config;
+ char *line, *cp, *from, *to, *ap;
+ size_t len, lineno = 0;
+ struct arglist al;
+
+ initarg(&al);
+ for (len = 0; len < argc; len++)
+ addarg(&al, argv[len], 0);
+
+ if ((config = fopen(_PATH_MAILERCONF, "r")) == NULL) {
+ addarg(&al, NULL, 0);
+ openlog("mailwrapper", LOG_PID, LOG_MAIL);
+ syslog(LOG_INFO, "can't open %s, using %s as default MTA",
+ _PATH_MAILERCONF, _PATH_DEFAULTMTA);
+ closelog();
+ execve(_PATH_DEFAULTMTA, al.argv, envp);
+ freearg(&al, 0);
+ err(1, "execing %s", _PATH_DEFAULTMTA);
+ /*NOTREACHED*/
+ }
+
+ for (;;) {
+ if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL) {
+ if (feof(config))
+ errx(1, "no mapping in %s", _PATH_MAILERCONF);
+ err(1, "can't 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, 0);
+ break;
+ }
+
+ free(line);
+ }
+
+ (void)fclose(config);
+
+ addarg(&al, NULL, 0);
+ execve(to, al.argv, envp);
+ freearg(&al, 0);
+ warn("execing %s", to);
+ free(line);
+ exit(1);
+ /*NOTREACHED*/
+parse_error:
+ freearg(&al, 0);
+ free(line);
+ errx(1, "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/makemap/Makefile b/usr.sbin/makemap/Makefile
new file mode 100644
index 0000000..bb7de3e
--- /dev/null
+++ b/usr.sbin/makemap/Makefile
@@ -0,0 +1,50 @@
+# @(#)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
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmdb)
+LIBSMDBDIR:= ${.OBJDIR}/../../lib/libsmdb
+.else
+LIBSMDBDIR!= cd ${.CURDIR}/../../lib/libsmdb; make -V .OBJDIR
+.endif
+LIBSMDB:= ${LIBSMDBDIR}/libsmdb.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+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..d465d53
--- /dev/null
+++ b/usr.sbin/memcontrol/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= memcontrol
+MAN= memcontrol.8
+
+.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..9eb8719
--- /dev/null
+++ b/usr.sbin/memcontrol/memcontrol.c
@@ -0,0 +1,343 @@
+/*-
+ * 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 <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct
+{
+ 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(char *what);
+
+struct
+{
+ char *cmd;
+ 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 %d 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("%qx/%qx %.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(int memfd, int argc, char *argv[])
+{
+ help(argv[1]);
+}
+
+static void
+help(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..359ffe2
--- /dev/null
+++ b/usr.sbin/mergemaster/mergemaster.8
@@ -0,0 +1,403 @@
+.\" Copyright (c) 1998-2003 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 February 5, 2001
+.Dt MERGEMASTER 8
+.Os
+.Sh NAME
+.Nm mergemaster
+.Nd merge configuration files, et al during an upgrade
+.Sh SYNOPSIS
+.Nm
+.Op Fl scrvahipCP
+.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 D Ar /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/etc/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
+.Xr cvs 1
+version $Id/$FreeBSD strings 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.
+.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 -p option to rebuild your password databases
+and recreate
+.Pa /etc/passwd .
+.Pp
+The script uses the owner and group id's
+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's 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, diff'ing every pair of files.
+This comparison is performed line by line,
+without regard to CVS $Id's.
+.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 -a makes
+-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 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 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.)
+.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 -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 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 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 diff's 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 FILES
+.Bl -tag -width $HOME/.mergemasterrc -compact
+.It Pa /etc/mergemaster.rc
+.It Pa $HOME/.mergemasterrc
+.El
+.Pp
+The
+.Nm
+utility will . (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
+#
+# Directory to install the temporary root environment into
+#TEMPROOT='/var/tmp/temproot'
+#
+# Strict comparison bypasses the CVS $Id tests and compares every file
+#STRICT=no
+#
+# Type of diff, such as unified, context, etc.
+#DIFF_FLAG='-u'
+#
+# Additional options for diff. This will get unset when using -s.
+#DIFF_OPTIONS='-I$\&FreeBSD:.*[$]' # Ignores CVS Id tags
+#
+# Verbose mode includes more details and additional checks
+#VERBOSE=
+#
+# Automatically install files that do not exist on the system already
+#AUTO_INSTALL=
+#
+# 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`
+#
+# Sourcedir is the directory to do the 'make' in (where the new files are)
+#SOURCEDIR='/usr/src/etc'
+#
+# The umask for mergemaster to compare the default file's modes to
+#NEW_UMASK=022
+#
+# Specify the destination directory for the installed files
+#DESTDIR=
+#
+# The following options have no command line overrides
+# 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
+#
+# Don't compare the old and new motd files
+#IGNORE_MOTD=yes
+#
+# 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 SEE ALSO
+.Xr cvs 1 ,
+.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/handbook/makeworld.html
+.%T The Cutting Edge (using make world)
+.%A Nik Clayton
+.Re
+.Sh DIAGNOSTICS
+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
+.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..6ec345a
--- /dev/null
+++ b/usr.sbin/mergemaster/mergemaster.sh
@@ -0,0 +1,1112 @@
+#!/bin/sh
+
+# mergemaster
+
+# Compare files created by /usr/src/etc/Makefile (or the directory
+# the user specifies) with the currently installed copies.
+
+# Copyright 1998-2004 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 [-scrvahipCP] [-m /path]'
+ echo ' [-t /path] [-d] [-u N] [-w N] [-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 ' -C Compare local rc.conf variables to the defaults'
+ echo ' -P Preserve files that are overwritten'
+ 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 ' -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 [ "${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
+
+# Check the command line options
+#
+while getopts ":ascrvhipCPm:t:du:w:D:" COMMAND_LINE_ARGUMENT ; do
+ case "${COMMAND_LINE_ARGUMENT}" in
+ 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
+
+echo ''
+
+# If the user has a pager defined, make sure we can run it
+#
+case "${DONT_CHECK_PAGER}" in
+'')
+ while ! type "${PAGER%% *}" >/dev/null && [ -n "${PAGER}" ]; 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
+ ;;
+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/etc}
+
+# 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
+ '') ;;
+ *)
+ make DESTDIR=${DESTDIR} distrib-dirs
+ ;;
+ esac
+ make DESTDIR=${TEMPROOT} distrib-dirs &&
+ MAKEOBJDIRPREFIX=${TEMPROOT}/usr/obj make obj &&
+ MAKEOBJDIRPREFIX=${TEMPROOT}/usr/obj make all &&
+ MAKEOBJDIRPREFIX=${TEMPROOT}/usr/obj 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}/master.passwd ${TEMPROOT}/etc &&
+ cp -p ${SOURCEDIR}/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
+ case "${IGNORE_MOTD}" in
+ '') ;;
+ *) rm -f ${TEMPROOT}/etc/motd
+ ;;
+ esac
+
+ # Avoid trying to update MAKEDEV if /dev is on a devfs
+ if /sbin/sysctl vfs.devfs.generation > /dev/null 2>&1 ; then
+ rm -f ${TEMPROOT}/dev/MAKEDEV ${TEMPROOT}/dev/MAKEDEV.local
+ fi
+
+ ;; # 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
+
+# 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}/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 isdn 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 isdn 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
+#
+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
+
+ install -m "${1}" "${2}" "${3}" &&
+ rm -f "${2}"
+}
+
+# 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
+ case "${1#.}" in
+ /dev/MAKEDEV)
+ NEED_MAKEDEV=yes
+ ;;
+ esac
+ do_install_and_rm "${FILE_MODE}" "${1}" "${DESTDIR}${INSTALL_DIR}"
+ fi
+ return $?
+}
+
+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? [y] '
+ read DELETE_STALE_RC_FILES
+ case "${DELETE_STALE_RC_FILES}" in
+ [nN])
+ echo ' *** Files will not be deleted'
+ ;;
+ *)
+ echo ' *** Deleting ... '
+ rm ${STALE_RC_FILES}
+ echo ' done.'
+ ;;
+ 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}/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. :)
+ #
+ 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 do way up there at the beginning of the comparison
+
+echo ''
+echo "*** Comparison complete"
+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
+
+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_MAKEDEV}" in
+'') ;;
+*)
+ echo ''
+ echo "*** You installed a new ${DESTDIR}/dev/MAKEDEV script, so make sure that you run"
+ echo " 'cd ${DESTDIR}/dev && /bin/sh MAKEDEV all' to rebuild your devices"
+ run_it_now "cd ${DESTDIR}/dev && /bin/sh MAKEDEV all"
+ ;;
+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%etc}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..e86fcee
--- /dev/null
+++ b/usr.sbin/mixer/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= mixer
+MAN= mixer.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8
new file mode 100644
index 0000000..4d73ac5
--- /dev/null
+++ b/usr.sbin/mixer/mixer.8
@@ -0,0 +1,172 @@
+.\" 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 January 9, 1997
+.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
+.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
+.Cm recsrc
+.Ar ...
+.Nm
+.Op Fl f Ar device
+.Op Fl 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, mic, cd, mix,
+pcm2, rec, igain, ogain, line1, line2, and line3.
+.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
+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..7abac46
--- /dev/null
+++ b/usr.sbin/mixer/mixer.c
@@ -0,0 +1,287 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/soundcard.h>
+
+const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
+
+void usage(int devmask, int recmask);
+int res_name(const char *name, int mask);
+void print_recsrc(int recsrc);
+
+void
+usage(int devmask, int recmask)
+{
+ int i, n;
+
+ printf("usage: mixer [-f device] [-s] [dev [+|-][voll[:[+|-]volr]] ...\n"
+ " mixer [-f device] [-s] recsrc ...\n"
+ " mixer [-f device] [-s] {^|+|-|=}rec rdev ... \n");
+ printf(" devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask) {
+ if (n)
+ printf(", ");
+ printf("%s", names[i]);
+ n = 1;
+ }
+ 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 = 1;
+ }
+ printf("\n");
+ exit(1);
+}
+
+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))
+ break;
+
+ return foo == SOUND_MIXER_NRDEVICES ? -1 : foo;
+}
+
+void
+print_recsrc(int recsrc)
+{
+ int i, n = 0;
+ fprintf(stderr, "Recording source: ");
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & recsrc) {
+ if (n)
+ fprintf(stderr, ", ");
+ fprintf(stderr, "%s", names[i]);
+ n = 1;
+ }
+ fprintf(stderr, "\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int foo, bar, baz, dev;
+ int devmask = 0, recmask = 0, recsrc = 0, orecsrc;
+ int dusage = 0, drecsrc = 0, shortflag = 0;
+ int l = 0, r = 0, t = 0;
+ char lstr[5], rstr[5];
+ int n = 0, lrel = 0, rrel = 0;
+ int ch;
+
+ char *name;
+
+ name = strdup("/dev/mixer");
+
+ if (!strcmp(argv[0], "mixer2"))
+ name = strdup("/dev/mixer1");
+ else if (!strcmp(argv[0], "mixer3"))
+ name = strdup("/dev/mixer2");
+
+ while ((ch = getopt(argc, argv, "f:s")) != -1)
+ switch (ch) {
+ case 'f':
+ name = strdup(optarg);
+ break;
+ case 's':
+ shortflag = 1;
+ break;
+ default:
+ dusage = 1;
+ }
+ argc -= (optind - 1);
+ argv += (optind - 1);
+
+ if ((baz = open(name, O_RDWR)) < 0)
+ err(1, "%s", name);
+ free(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; foo < SOUND_MIXER_NRDEVICES; foo++) {
+ if (!((1 << foo) & devmask))
+ continue;
+ if (ioctl(baz, MIXER_READ(foo),&bar)== -1) {
+ warn("MIXER_READ");
+ continue;
+ }
+ if (shortflag)
+ printf("%s %d:%d ", names[foo], bar & 0x7f,
+ (bar >> 8) & 0x7f);
+ else
+ printf("Mixer %-8s is currently set to %3d:%d\n",
+ names[foo], bar & 0x7f, (bar >> 8) & 0x7f);
+ }
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ print_recsrc(recsrc);
+ return(0);
+ }
+
+ argc--; argv++;
+
+ while ((argc > 0) && (dusage == 0)) {
+ if (!strcmp("recsrc", *argv)) {
+ drecsrc = 1;
+ argc--; argv++;
+ continue;
+ } else if (argc > 1 && !strcmp("rec", *argv + 1)) {
+ 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;
+ }
+
+#define issign(c) (((c) == '+') || ((c) == '-'))
+
+ if (argc > 1) {
+ n = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
+ if (n > 0) {
+ if (issign(lstr[0]))
+ lrel = rrel = 1;
+ l = atoi(lstr);
+ }
+ if (n > 1) {
+ rrel = 0;
+ if (issign(rstr[0]))
+ rrel = 1;
+ r = atoi(rstr);
+ }
+ }
+
+ switch(argc > 1 ? n : t) {
+ case 0:
+ if (ioctl(baz, MIXER_READ(dev),&bar)== -1) {
+ warn("MIXER_READ");
+ argc--; argv++;
+ continue;
+ }
+ if (shortflag)
+ printf("%s %d:%d ", names[dev], bar & 0x7f,
+ (bar >> 8) & 0x7f);
+ 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;
+ 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;
+
+ 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);
+ /* Not reached */
+ }
+
+ if (orecsrc != recsrc)
+ if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_WRITE_RECSRC");
+
+ if (drecsrc) {
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ print_recsrc(recsrc);
+ }
+
+ close(baz);
+
+ exit(0);
+}
diff --git a/usr.sbin/mld6query/Makefile b/usr.sbin/mld6query/Makefile
new file mode 100644
index 0000000..7100520
--- /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+= -DINET6 -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..cc361d0
--- /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 RETURN VALUES
+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..a364767
--- /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_CHECKASYNC");
+ }
+ }
+ 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..5cbf7b6
--- /dev/null
+++ b/usr.sbin/mlxcontrol/interface.c
@@ -0,0 +1,287 @@
+/*-
+ * 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;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0)
+ return;
+
+ for (i = -1; ;) {
+ /* Get the unit number of the next child device */
+ if (ioctl(fd, MLX_NEXT_CHILD, &i) < 0)
+ 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..3803fb6
--- /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
+(eg. 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
+(eg. 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 BUGS
+The
+.Ar config
+command does not yet support modifying system drive configuration.
+.Pp
+Error log extraction is not yet supported.
+.Sh AUTHORS
+The mlxcontrol utility was written by
+.An Michael Smith
+.Aq msmith@FreeBSD.org .
diff --git a/usr.sbin/mlxcontrol/mlxcontrol.h b/usr.sbin/mlxcontrol/mlxcontrol.h
new file mode 100644
index 0000000..f0225b5
--- /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", __FUNCTION__ , ##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..afc68e0
--- /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 aren't 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
+Don't 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 BUGS
+to number a few
+.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
diff --git a/usr.sbin/mount_nwfs/mount_nwfs.c b/usr.sbin/mount_nwfs/mount_nwfs.c
new file mode 100644
index 0000000..c784a9a
--- /dev/null
+++ b/usr.sbin/mount_nwfs/mount_nwfs.c
@@ -0,0 +1,366 @@
+/*
+ * 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;
+ int mib[2];
+ 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;
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_WALLCLOCK;
+ len = sizeof(wall_clock);
+ if (sysctl(mib, 2, &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..56667f1
--- /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 getmntopts.c pt_conf.c \
+ pt_exec.c pt_file.c pt_tcp.c pt_tcplisten.c
+MAN= mount_portalfs.8
+
+MOUNT= ${.CURDIR}/../../sbin/mount
+CFLAGS+=-I${MOUNT}
+WARNS?= 0
+
+.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..2e8b13c
--- /dev/null
+++ b/usr.sbin/mount_portalfs/activate.c
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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(pcr, key, v, so, fdp)
+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(so, pcr, key, klen)
+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 <= sizeof(*pcr))
+ return (EINVAL);
+
+ n -= sizeof(*pcr);
+ key[n] = '\0';
+
+ return (0);
+}
+
+static void send_reply(so, fd, error)
+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, 2) < 0)
+ syslog(LOG_ERR, "shutdown: %s", strerror(errno));
+#endif
+ /*
+ * Throw away the open file descriptor
+ */
+ (void) close(fd);
+}
+
+void activate(q, so)
+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..f3a65e6
--- /dev/null
+++ b/usr.sbin/mount_portalfs/conf.c
@@ -0,0 +1,338 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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(elem, pred)
+qelem *elem, *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(elem)
+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(siz)
+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(p0, q0)
+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(cline, lno)
+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(p)
+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(q0, xq)
+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(q0, fp)
+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(q, conf)
+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(q0, key)
+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/mount_portalfs.8 b/usr.sbin/mount_portalfs/mount_portalfs.8
new file mode 100644
index 0000000..b747466
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.8
@@ -0,0 +1,152 @@
+.\"
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)mount_portal.8 8.3 (Berkeley) 3/27/94
+.\" $FreeBSD$
+.\"
+.Dd March 27, 1994
+.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 tcplisten ,
+.Pa tcp
+and
+.Pa fs .
+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.
+The
+.Pa tcp
+namespace takes a hostname and a port (slash separated) and
+creates an open TCP/IP connection.
+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.
+.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/
+.Ed
+.Sh FILES
+.Bl -tag -width /p/* -compact
+.It Pa /p/*
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.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..5ed2145
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+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,
+ { NULL }
+};
+
+static void usage(void) __dead2;
+
+static sig_atomic_t readcf; /* Set when SIGHUP received */
+
+static void sighup(sig)
+int sig;
+{
+ readcf ++;
+}
+
+static void sigchld(sig)
+int sig;
+{
+ 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(argc, argv)
+ 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];
+
+ /* 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;
+ int rc;
+
+ /*
+ * 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..1672085
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pathnames.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ * 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/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..53a07a9
--- /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
+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..46c6afe
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portald.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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 {
+ 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_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);
diff --git a/usr.sbin/mount_portalfs/pt_conf.c b/usr.sbin/mount_portalfs/pt_conf.c
new file mode 100644
index 0000000..1f8bbc3
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_conf.c
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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 },
+ { "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..c5a4f56
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_exec.c
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ 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..2aed8d0
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_file.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ int fd;
+ char pbuf[MAXPATHLEN];
+ int error;
+ gid_t gidset[NGROUPS];
+ int i;
+
+ 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
+
+ for (i = 0; i < pcr->pcr_ngroups; i++)
+ gidset[i] = pcr->pcr_groups[i];
+
+ if (setgroups(pcr->pcr_ngroups, gidset) < 0)
+ return (errno);
+
+ if (seteuid(pcr->pcr_uid) < 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 (seteuid((uid_t) 0) < 0) { /* XXX - should reset gidset too */
+ error = errno;
+ syslog(LOG_ERR, "setcred: %s", strerror(error));
+ 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_tcp.c b/usr.sbin/mount_portalfs/pt_tcp.c
new file mode 100644
index 0000000..53136e5
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_tcp.c
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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(pcr, key, v, kso, fdp)
+ struct portal_cred *pcr;
+ char *key;
+ char **v;
+ int kso;
+ 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 >= 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..a7674bb
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_tcplisten.c
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ * 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.
+ *
+ * @(#)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(pcr, key, v, kso, fdp)
+ struct portal_cred *pcr;
+ char *key;
+ char **v;
+ int kso;
+ 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 any = 0;
+ struct sockaddr_in sain;
+
+ q = strchr(p, '/');
+ if (q == 0 || q - p >= 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..482b955
--- /dev/null
+++ b/usr.sbin/mount_smbfs/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+PROG= mount_smbfs
+SRCS= mount_smbfs.c getmntopts.c
+WARNS?= 0
+MAN= mount_smbfs.8
+
+MOUNTDIR= ${.CURDIR}/../../sbin/mount
+CONTRIBDIR= ${.CURDIR}/../../contrib/smbfs
+CFLAGS+= -DSMBFS -I${MOUNTDIR} -I${CONTRIBDIR}/include
+
+LDADD= -lsmb
+DPADD= ${LIBSMB}
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv (see the -E option).
+#
+NOSHARED?= NO
+
+.PATH: ${CONTRIBDIR}/mount_smbfs
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/Makefile b/usr.sbin/mountd/Makefile
new file mode 100644
index 0000000..d015160
--- /dev/null
+++ b/usr.sbin/mountd/Makefile
@@ -0,0 +1,9 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $FreeBSD$
+
+PROG= mountd
+MAN= exports.5 netgroup.5 mountd.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/exports.5 b/usr.sbin/mountd/exports.5
new file mode 100644
index 0000000..e20948f
--- /dev/null
+++ b/usr.sbin/mountd/exports.5
@@ -0,0 +1,361 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)exports.5 8.3 (Berkeley) 3/29/95
+.\" $FreeBSD$
+.\"
+.Dd March 29, 1995
+.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 ``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 "." or ".." 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 No = 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 No = 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 No = Sy user
+.Sm on
+or
+.Sm off
+.Fl mapall No = 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
+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 = Sy 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 ``dot'' addresses may be used in place of names.)
+The second way is to specify a ``netgroup'' as defined in the 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 ``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 No = Sy netname
+.Sm on
+and optionally
+.Sm off
+.Fl mask No = Sy netmask .
+.Sm on
+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
+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
+kill -s HUP `cat /var/run/mountd.pid`
+.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
+/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
+.Ed
+.Pp
+Given that
+.Sy /usr ,
+.Sy /u
+and
+.Sy /u2
+are
+local file system mount points, the above example specifies the following:
+.Pp
+.Sy /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 ``friends'' can mount either /usr
+or /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 ``daemon'';
+it is exported to the rest of the world as read-only with
+all users mapped to the user and groups associated with ``nobody''.
+.Pp
+.Sy /u
+is exported to all hosts on the subnetwork
+.Em 131.104.48
+with root mapped to the uid for ``bin'' and with no group access.
+.Pp
+.Sy /u2
+is exported to the hosts in ``friends'' with root mapped to uid and groups
+associated with ``root'';
+it is exported to all hosts on network ``cis-net'' allowing mounts at any
+directory within /u2.
+.Pp
+The file system rooted at
+.Sy /cdrom
+will exported read-only to the entire network 192.168.33.0/24, including
+all its subdirectories.
+Since
+.Sy /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
+.Sy /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
+.Sy /cdrom ,
+it would export the (normally empty) directory
+.Sy /cdrom
+of the root file system instead.
+.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..61ae819
--- /dev/null
+++ b/usr.sbin/mountd/mountd.8
@@ -0,0 +1,164 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd April 28, 1995
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm
+.Op Fl 2dlnr
+.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.
+.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.
+.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..a52ee0d
--- /dev/null
+++ b/usr.sbin/mountd/mountd.c
@@ -0,0 +1,2548 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /*not lint*/
+
+#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 <ufs/ufs/ufsmount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <fs/ntfs/ntfsmount.h>
+#include <isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
+
+#ifndef MOUNTDLOCK
+#define MOUNTDLOCK "/var/run/mountd.lock"
+#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;
+};
+/* 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;
+};
+
+/* 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 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 exname[MAXPATHLEN];
+struct xucred def_anon = {
+ XUCRED_VERSION,
+ (uid_t)-2,
+ 1,
+ { (gid_t)-2 },
+ NULL
+};
+int force_v2 = 0;
+int resvport_only = 1;
+int dir_only = 1;
+int dolog = 0;
+int got_sighup = 0;
+
+int opt_flags;
+static int have_v6 = 1;
+#ifdef NI_WITHSCOPEID
+static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+static const int ninumeric = NI_NUMERICHOST;
+#endif
+
+int mountdlockfd;
+/* 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
+
+#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 sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ char *endptr;
+ SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
+ struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
+ int udpsock, tcpsock, udp6sock, tcp6sock;
+ int xcreated = 0, s;
+ int maxrec = RPC_MAXDATASIZE;
+ int one = 1;
+ int c, r;
+ in_port_t svcport = 0;
+
+ udp6conf = tcp6conf = NULL;
+ udp6sock = tcp6sock = 0;
+
+ /* Check that another mountd isn't already running. */
+ if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
+ err(1, "%s", MOUNTDLOCK);
+
+ if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
+ errx(1, "another mountd is already running. Aborting");
+ 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, "2dlnp: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();
+ break;
+ default:
+ usage();
+ };
+ argc -= optind;
+ argv += optind;
+ grphead = (struct grouplist *)NULL;
+ exphead = (struct exportlist *)NULL;
+ mlhead = (struct mountlist *)NULL;
+ if (argc == 1) {
+ strncpy(exname, *argv, MAXPATHLEN-1);
+ exname[MAXPATHLEN-1] = '\0';
+ } else
+ strcpy(exname, _PATH_EXPORTS);
+ 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);
+ { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
+ if (pidfile != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ fclose(pidfile);
+ }
+ }
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
+ udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ udpconf = getnetconfigent("udp");
+ tcpconf = getnetconfigent("tcp");
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ if (!have_v6)
+ goto skip_v6;
+ udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ /*
+ * 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 (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
+ IPV6_V6ONLY, &one, sizeof one) < 0) {
+ syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
+ exit(1);
+ }
+ if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
+ IPV6_V6ONLY, &one, sizeof one) < 0) {
+ syslog(LOG_ERR, "can't disable v4-in-v6 on TCP socket");
+ exit(1);
+ }
+ udp6conf = getnetconfigent("udp6");
+ tcp6conf = getnetconfigent("tcp6");
+
+skip_v6:
+ 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 (svcport != 0) {
+ bzero(&sin, sizeof(struct sockaddr_in));
+ sin.sin_len = sizeof(struct sockaddr_in);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(svcport);
+
+ bzero(&sin6, sizeof(struct sockaddr_in6));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(svcport);
+ }
+ if (udpsock != -1 && udpconf != NULL) {
+ if (svcport != 0) {
+ r = bindresvport(udpsock, &sin);
+ if (r != 0) {
+ syslog(LOG_ERR, "bindresvport: %m");
+ exit(1);
+ }
+ } else
+ (void)bindresvport(udpsock, NULL);
+ udptransp = svc_dg_create(udpsock, 0, 0);
+ if (udptransp != NULL) {
+ if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, udpconf))
+ syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, udpconf))
+ syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create UDP services");
+
+ }
+ if (tcpsock != -1 && tcpconf != NULL) {
+ if (svcport != 0) {
+ r = bindresvport(tcpsock, &sin);
+ if (r != 0) {
+ syslog(LOG_ERR, "bindresvport: %m");
+ exit(1);
+ }
+ } else
+ (void)bindresvport(tcpsock, NULL);
+ listen(tcpsock, SOMAXCONN);
+ tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (tcptransp != NULL) {
+ if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, tcpconf))
+ syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, tcpconf))
+ syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create TCP service");
+
+ }
+ if (have_v6 && udp6sock != -1 && udp6conf != NULL) {
+ if (svcport != 0) {
+ r = bindresvport_sa(udp6sock,
+ (struct sockaddr *)&sin6);
+ if (r != 0) {
+ syslog(LOG_ERR, "bindresvport_sa: %m");
+ exit(1);
+ }
+ } else
+ (void)bindresvport_sa(udp6sock, NULL);
+ udp6transp = svc_dg_create(udp6sock, 0, 0);
+ if (udp6transp != NULL) {
+ if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, udp6conf))
+ syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, udp6conf))
+ syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create UDP6 service");
+
+ }
+ if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) {
+ if (svcport != 0) {
+ r = bindresvport_sa(tcp6sock,
+ (struct sockaddr *)&sin6);
+ if (r != 0) {
+ syslog(LOG_ERR, "bindresvport_sa: %m");
+ exit(1);
+ }
+ } else
+ (void)bindresvport_sa(tcp6sock, NULL);
+ listen(tcp6sock, SOMAXCONN);
+ tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (tcp6transp != NULL) {
+ if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
+ mntsrv, tcp6conf))
+ syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service");
+ else
+ xcreated++;
+ if (!force_v2) {
+ if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, tcp6conf))
+ syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service");
+ else
+ xcreated++;
+ }
+ } else
+ syslog(LOG_WARNING, "can't create TCP6 service");
+
+ }
+ 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);
+ }
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mountd [-2] [-d] [-l] [-n] [-p <port>] [-r] "
+ "[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;
+ struct addrinfo *ai;
+ 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);
+ ai = NULL;
+ 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;
+ }
+ 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;
+
+ 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);
+ auth = RPCAUTH_UNIX;
+ 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
+ */
+void
+get_exportlist()
+{
+ struct exportlist *ep, *ep2;
+ struct grouplist *grp, *tgrp;
+ struct exportlist **epp;
+ struct dirlist *dirhead;
+ struct statfs fsb, *fsp;
+ struct xucred anon;
+ char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
+
+ dirp = NULL;
+ dirplen = 0;
+
+ /*
+ * 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
+ * instead of just "ufs".
+ */
+ num = getmntinfo(&fsp, MNT_NOWAIT);
+ for (i = 0; i < num; i++) {
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct msdosfs_args da;
+ struct ntfs_args na;
+ } targs;
+
+ if (!strcmp(fsp->f_fstypename, "ufs") ||
+ !strcmp(fsp->f_fstypename, "msdosfs") ||
+ !strcmp(fsp->f_fstypename, "ntfs") ||
+ !strcmp(fsp->f_fstypename, "cd9660")) {
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_fstypename, fsp->f_mntonname,
+ fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 &&
+ errno != ENOENT)
+ syslog(LOG_ERR,
+ "can't delete exports for %s: %m",
+ fsp->f_mntonname);
+ }
+ fsp++;
+ }
+
+ /*
+ * Read in the exports file and build the list, calling
+ * mount() as we go along to push the export rules into the kernel.
+ */
+ if ((exp_file = fopen(exname, "r")) == NULL) {
+ syslog(LOG_ERR, "can't open %s", exname);
+ exit(2);
+ }
+ 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;
+ }
+ }
+ fclose(exp_file);
+}
+
+/*
+ * 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 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 {
+ 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, ninumeric) != 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 mount syscall with the update flag to push the export info into
+ * the kernel.
+ */
+int
+do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
+ 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 *cp = NULL;
+ int done;
+ char savedc = '\0';
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct msdosfs_args da;
+ struct ntfs_args na;
+ } args;
+
+ bzero(&args, sizeof args);
+ /* XXX, we assume that all xx_args look like ufs_args. */
+ args.ua.fspec = 0;
+ eap = &args.ua.export;
+
+ 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;
+ done = FALSE;
+ 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 = args.ua.export.ex_addr->sa_len;
+ eap->ex_mask =
+ (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
+ eap->ex_masklen = args.ua.export.ex_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:
+ return(0);
+ break;
+ default:
+ syslog(LOG_ERR, "bad grouptype");
+ if (cp)
+ *cp = savedc;
+ return (1);
+ };
+
+ /*
+ * 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".
+ */
+ while (mount(fsb->f_fstypename, dirp,
+ fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
+ if (cp)
+ *cp-- = savedc;
+ else
+ cp = dirp + dirplen - 1;
+ if (opt_flags & OP_QUIET)
+ return (1);
+ if (errno == EPERM) {
+ if (debug)
+ warnx("can't change attributes for %s",
+ dirp);
+ syslog(LOG_ERR,
+ "can't change attributes for %s", dirp);
+ return (1);
+ }
+ 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);
+ return (1);
+ }
+ /* 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", dirp);
+ return (1);
+ }
+ 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", dirp);
+ return (1);
+ }
+ }
+skip:
+ if (ai != NULL)
+ ai = ai->ai_next;
+ if (ai == NULL)
+ done = TRUE;
+ }
+ if (cp)
+ *cp = savedc;
+ return (0);
+}
+
+/*
+ * 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, ninumeric) == 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;
+ int ngroups, groups[NGROUPS + 1];
+
+ 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");
+ /*
+ * Convert from int's to gid_t's and 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;
+{
+ close(mountdlockfd);
+ unlink(MOUNTDLOCK);
+ 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..ba58b2c
--- /dev/null
+++ b/usr.sbin/mountd/netgroup.5
@@ -0,0 +1,194 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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 SEE ALSO
+.Xr getnetgrent 3 ,
+.Xr exports 5
+.Sh COMPATIBILITY
+The file format is compatible with that of various vendors, however it
+appears that not all vendors use an identical format.
+.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..aa1c555
--- /dev/null
+++ b/usr.sbin/mountd/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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/5/93
+ */
+#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..c9c1b31
--- /dev/null
+++ b/usr.sbin/moused/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= moused
+MAN= moused.8
+
+#BINMODE=4555
+#INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/moused/moused.8 b/usr.sbin/moused/moused.8
new file mode 100644
index 0000000..0636cb4
--- /dev/null
+++ b/usr.sbin/moused/moused.8
@@ -0,0 +1,681 @@
+.\" 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 April 1, 2000
+.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 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 3 Op Fl E Ar timeout
+.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 ``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
+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 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 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.
+.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/cuaa0 ,
+.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 ``unknown''
+or ``generic''.
+.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 `='.
+.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
+.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/cuaa0
+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.
+.Pa
+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 ``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 ``MS''
+and ``PC'', or ``2'' and ``3''.
+``MS'' or ``2'' usually mean the
+.Ar microsoft
+protocol.
+``PC'' or ``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 Ar _selected_port_ -t Ar _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/cuaa0 -i type
+.Pp
+Let the
+.Nm
+utility determine the protocol type of the mouse at the serial port
+.Pa /dev/cuaa0 .
+If successful, the command will print the type, otherwise it will say
+``unknown''.
+.Pp
+.Dl moused -p /dev/cuaa0
+.Dl vidcontrol -m on
+.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.
+.Pp
+.Dl moused -p /dev/mouse -t microsoft
+.Dl vidcontrol -m on
+.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.
+.Sh CAVEATS
+The
+.Nm
+utility does not currently work with the alternative console driver
+.Xr pcvt 4 .
+.Pp
+Many pad devices behave as if the first (left) button were pressed if
+the user `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 ``-m 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:
+``-m 2=3''.
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr vidcontrol 1 ,
+.Xr keyboard 4 ,
+.Xr mse 4 ,
+.Xr pcvt 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 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 .
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.2 .
diff --git a/usr.sbin/moused/moused.c b/usr.sbin/moused/moused.c
new file mode 100644
index 0000000..b08365f
--- /dev/null
+++ b/usr.sbin/moused/moused.c
@@ -0,0 +1,3026 @@
+/**
+ ** 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/linker.h>
+#include <sys/module.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 <limits.h>
+#include <setjmp.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>
+
+#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 */
+
+/* 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 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)
+
+#define debug(fmt, args...) do { \
+ if (debug && nodaemon) \
+ warnx(fmt, ##args); \
+} while (0)
+
+#define logerr(e, fmt, args...) do { \
+ log_or_warn(LOG_DAEMON | LOG_ERR, errno, fmt, ##args); \
+ exit(e); \
+} while (0)
+
+#define logerrx(e, fmt, args...) do { \
+ log_or_warn(LOG_DAEMON | LOG_ERR, 0, fmt, ##args); \
+ exit(e); \
+} while (0)
+
+#define logwarn(fmt, args...) \
+ log_or_warn(LOG_DAEMON | LOG_WARNING, errno, fmt, ##args)
+
+#define logwarnx(fmt, args...) \
+ log_or_warn(LOG_DAEMON | LOG_WARNING, 0, fmt, ##args)
+
+/* structures */
+
+/* symbol table entry */
+typedef struct {
+ char *name;
+ int val;
+ int val2;
+} symtab_t;
+
+/* serial PnP ID string */
+typedef struct {
+ int revision; /* PnP revision, 100 for 1.00 */
+ char *eisaid; /* EISA ID including mfr ID and product ID */
+ char *serial; /* serial No, optional */
+ 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 identify = ID_NONE;
+int extioctl = FALSE;
+char *pidfile = "/var/run/moused.pid";
+
+/* local variables */
+
+/* interface (the table must be ordered by MOUSE_IF_XXX in mouse.h) */
+static symtab_t rifs[] = {
+ { "serial", MOUSE_IF_SERIAL },
+ { "bus", MOUSE_IF_BUS },
+ { "inport", MOUSE_IF_INPORT },
+ { "ps/2", MOUSE_IF_PS2 },
+ { "sysmouse", MOUSE_IF_SYSMOUSE },
+ { "usb", MOUSE_IF_USB },
+ { NULL, MOUSE_IF_UNKNOWN },
+};
+
+/* types (the table must be ordered by MOUSE_PROTO_XXX in mouse.h) */
+static char *rnames[] = {
+ "microsoft",
+ "mousesystems",
+ "logitech",
+ "mmseries",
+ "mouseman",
+ "busmouse",
+ "inportmouse",
+ "ps/2",
+ "mmhitab",
+ "glidepoint",
+ "intellimouse",
+ "thinkingmouse",
+ "sysmouse",
+ "x10mouseremote",
+ "kidspad",
+ "versapad",
+ "jogdial",
+#if notyet
+ "mariqua",
+#endif
+ NULL
+};
+
+/* models */
+static symtab_t rmodels[] = {
+ { "NetScroll", MOUSE_MODEL_NETSCROLL },
+ { "NetMouse/NetScroll Optical", MOUSE_MODEL_NET },
+ { "GlidePoint", MOUSE_MODEL_GLIDEPOINT },
+ { "ThinkingMouse", MOUSE_MODEL_THINK },
+ { "IntelliMouse", MOUSE_MODEL_INTELLI },
+ { "EasyScroll/SmartScroll", MOUSE_MODEL_EASYSCROLL },
+ { "MouseMan+", MOUSE_MODEL_MOUSEMANPLUS },
+ { "Kidspad", MOUSE_MODEL_KIDSPAD },
+ { "VersaPad", MOUSE_MODEL_VERSAPAD },
+ { "IntelliMouse Explorer", MOUSE_MODEL_EXPLORER },
+ { "4D Mouse", MOUSE_MODEL_4D },
+ { "4D+ Mouse", MOUSE_MODEL_4DPLUS },
+ { "generic", MOUSE_MODEL_GENERIC },
+ { NULL, MOUSE_MODEL_UNKNOWN },
+};
+
+/* 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
+};
+
+static struct rodentparam {
+ int flags;
+ 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 */
+} 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,
+};
+
+/* button status */
+struct button_state {
+ int count; /* 0: up, 1: single click, 2: double click,... */
+ struct timeval tv; /* 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 timeval mouse_button_state_tv;
+static int mouse_move_delayed;
+
+static jmp_buf env;
+
+/* function prototypes */
+
+static void moused(void);
+static void hup(int sig);
+static void cleanup(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 char *r_if(int type);
+static char *r_name(int type);
+static 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, char *s, int len);
+static char *gettokenname(symtab_t *tab, int val);
+
+static void mremote_serversetup();
+static void mremote_clientchg(int add);
+
+static int kidspad(u_char rxc, mousestatus_t *act);
+
+static int usbmodule(void);
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int i;
+ int j;
+ int retry;
+
+ for (i = 0; i < MOUSE_MAXBUTTON; ++i)
+ mstate[i] = &bstate[i];
+
+ while ((c = getopt(argc, argv, "3C:DE:F:I:PRS: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 acceleration argument '%s'", optarg);
+ usage();
+ }
+
+ if (i == 1)
+ rodent.accely = rodent.accelx;
+
+ 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 'I':
+ pidfile = optarg;
+ 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':
+ if (strcmp(optarg, "auto") == 0) {
+ rodent.rtype = MOUSE_PROTO_UNKNOWN;
+ rodent.flags &= ~NoPnP;
+ rodent.level = -1;
+ break;
+ }
+ for (i = 0; rnames[i]; 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])
+ break;
+ warnx("no such mouse type `%s'", optarg);
+ usage();
+
+ 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);
+ 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)
+{
+ struct kld_file_stat fstat;
+ struct module_stat mstat;
+ int fileid, modid;
+ int loaded;
+
+ for (loaded = 0, fileid = kldnext(0); !loaded && fileid > 0;
+ fileid = kldnext(fileid)) {
+ fstat.version = sizeof(fstat);
+ if (kldstat(fileid, &fstat) < 0)
+ continue;
+ if (strncmp(fstat.name, "uhub/ums", 8) == 0) {
+ loaded = 1;
+ break;
+ }
+ for (modid = kldfirstmod(fileid); modid > 0;
+ modid = modfnext(modid)) {
+ mstat.version = sizeof(mstat);
+ if (modstat(modid, &mstat) < 0)
+ continue;
+ if (strncmp(mstat.name, "uhub/ums", 8) == 0) {
+ loaded = 1;
+ break;
+ }
+ }
+ }
+ if (!loaded) {
+ if (kldload("ums") != -1)
+ return 1;
+ if (errno != EEXIST)
+ logerr(1, "unable to load USB mouse driver");
+ }
+ return 0;
+}
+
+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;
+ FILE *fp;
+ 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) {
+ if (daemon(0, 0)) {
+ logerr(1, "failed to become a daemon");
+ } else {
+ background = TRUE;
+ fp = fopen(pidfile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+ }
+ }
+
+ /* clear mouse data */
+ bzero(&action0, sizeof(action0));
+ bzero(&action, sizeof(action));
+ bzero(&action2, sizeof(action2));
+ bzero(&mouse, sizeof(mouse));
+ mouse_button_state = S0;
+ gettimeofday(&mouse_button_state_tv, NULL);
+ mouse_move_delayed = 0;
+ for (i = 0; i < MOUSE_MAXBUTTON; ++i) {
+ bstate[i].count = 0;
+ bstate[i].tv = mouse_button_state_tv;
+ }
+ for (i = 0; i < sizeof(zstate)/sizeof(zstate[0]); ++i) {
+ zstate[i].count = 0;
+ zstate[i].tv = mouse_button_state_tv;
+ }
+
+ /* 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;
+ 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 (extioctl) {
+ r_click(&action2);
+ if (action2.flags & MOUSE_POSCHANGED) {
+ mouse.operation = MOUSE_MOTION_EVENT;
+ mouse.u.data.buttons = action2.button;
+ mouse.u.data.x = action2.dx * rodent.accelx;
+ mouse.u.data.y = action2.dy * rodent.accely;
+ mouse.u.data.z = action2.dz;
+ if (debug < 2)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+ } else {
+ mouse.operation = MOUSE_ACTION;
+ mouse.u.data.buttons = action2.button;
+ mouse.u.data.x = action2.dx * rodent.accelx;
+ mouse.u.data.y = action2.dy * rodent.accely;
+ mouse.u.data.z = action2.dz;
+ if (debug < 2)
+ 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)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+ }
+ }
+ }
+ /* NOT REACHED */
+}
+
+static void
+hup(int sig)
+{
+ longjmp(env, 1);
+}
+
+static void
+cleanup(int sig)
+{
+ if (rodent.rtype == MOUSE_PROTO_X10MOUSEREM)
+ unlink(_PATH_MOUSEREMOTE);
+ exit(0);
+}
+
+/**
+ ** usage
+ **
+ ** Complain, and free the CPU for more worthy tasks
+ **/
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: moused [-DRcdfs] [-I file] [-F rate] [-r resolution] [-S baudrate]",
+ " [-a X [,Y]] [-C threshold] [-m N=M] [-w N] [-z N]",
+ " [-t <mousetype>] [-3 [-E timeout]] -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 >= 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 char *
+r_if(int iftype)
+{
+ char *s;
+
+ s = gettokenname(rifs, iftype);
+ return (s == NULL) ? "unknown" : s;
+}
+
+static char *
+r_name(int type)
+{
+ return ((type == MOUSE_PROTO_UNKNOWN)
+ || (type > sizeof(rnames)/sizeof(rnames[0]) - 1))
+ ? "unknown" : rnames[type];
+}
+
+static char *
+r_model(int model)
+{
+ char *s;
+
+ s = gettokenname(rmodels, model);
+ return (s == NULL) ? "unknown" : s;
+}
+
+static void
+r_init(void)
+{
+ unsigned char buf[16]; /* scrach buffer */
+ fd_set fds;
+ 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) ;
+
+ /*
+ * 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)
+ gettimeofday(&mouse_button_state_tv, NULL);
+ 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 timeval tv;
+ struct timeval tv1;
+ struct timeval tv2;
+ struct timeval tv3;
+ int button;
+ int mask;
+ int i;
+
+ mask = act->flags & MOUSE_BUTTONS;
+#if 0
+ if (mask == 0)
+ return;
+#endif
+
+ gettimeofday(&tv1, NULL);
+
+ /* double click threshold */
+ tv2.tv_sec = rodent.clickthreshold/1000;
+ tv2.tv_usec = (rodent.clickthreshold%1000)*1000;
+ timersub(&tv1, &tv2, &tv);
+ debug("tv: %ld %ld", tv.tv_sec, tv.tv_usec);
+
+ /* 3 button emulation timeout */
+ tv2.tv_sec = rodent.button2timeout/1000;
+ tv2.tv_usec = (rodent.button2timeout%1000)*1000;
+ timersub(&tv1, &tv2, &tv3);
+
+ button = MOUSE_BUTTON1DOWN;
+ for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
+ if (mask & 1) {
+ if (act->button & button) {
+ /* the button is down */
+ debug(" : %ld %ld",
+ bstate[i].tv.tv_sec, bstate[i].tv.tv_usec);
+ if (timercmp(&tv, &bstate[i].tv, >)) {
+ bstate[i].count = 1;
+ } else {
+ ++bstate[i].count;
+ }
+ bstate[i].tv = tv1;
+ } else {
+ /* the button is up */
+ bstate[i].tv = tv1;
+ }
+ } else {
+ if (act->button & button) {
+ /* the button has been down */
+ if (timercmp(&tv3, &bstate[i].tv, >)) {
+ bstate[i].count = 1;
+ bstate[i].tv = tv1;
+ 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 timeval tv;
+ struct timeval tv1;
+ struct timeval tv2;
+
+ if (states[mouse_button_state].timeout)
+ return TRUE;
+ gettimeofday(&tv1, NULL);
+ tv2.tv_sec = rodent.button2timeout/1000;
+ tv2.tv_usec = (rodent.button2timeout%1000)*1000;
+ timersub(&tv1, &tv2, &tv);
+ return timercmp(&tv, &mouse_button_state_tv, >);
+}
+
+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)
+ 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;
+ 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, 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 char *
+gettokenname(symtab_t *tab, int val)
+{
+ int i;
+
+ for (i = 0; tab[i].name != NULL; ++i) {
+ if (tab[i].val == val)
+ return tab[i].name;
+ }
+ return NULL;
+}
+
+
+/*
+ * 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 timeval 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 ;
+ gettimeofday(&now, NULL);
+ 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 void
+mremote_serversetup()
+{
+ 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;
+ int ad_len, 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..e8ff5ad
--- /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 shouldn't 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..36d8089
--- /dev/null
+++ b/usr.sbin/mptable/mptable.c
@@ -0,0 +1,1114 @@
+/*
+ * 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 */
+
+#define VMAJOR 2
+#define VMINOR 0
+#define VDELTA 15
+
+/*
+ * 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, version %d.%d.%d\n", VMAJOR, VMINOR, VDELTA );
+
+ 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/mrouted/LICENSE b/usr.sbin/mrouted/LICENSE
new file mode 100644
index 0000000..ef7da47
--- /dev/null
+++ b/usr.sbin/mrouted/LICENSE
@@ -0,0 +1,48 @@
+
+The mrouted program is covered by the following license. Use of the
+mrouted program represents acceptance of these terms and conditions.
+
+1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license
+to use, copy and modify the computer software ``mrouted'' (hereinafter
+called the ``Program''), upon the terms and conditions hereinafter set
+out and until Licensee discontinues use of the Licensed Program.
+
+2. LICENSEE acknowledges that the Program is a research tool still in
+the development state, that it is being supplied ``as is,'' without any
+accompanying services from STANFORD, and that this license is entered
+into in order to encourage scientific collaboration aimed at further
+development and application of the Program.
+
+3. LICENSEE may copy the Program and may sublicense others to use object
+code copies of the Program or any derivative version of the Program.
+All copies must contain all copyright and other proprietary notices found
+in the Program as provided by STANFORD. Title to copyright to the
+Program remains with STANFORD.
+
+4. LICENSEE may create derivative versions of the Program. LICENSEE
+hereby grants STANFORD a royalty-free license to use, copy, modify,
+distribute and sublicense any such derivative works. At the time
+LICENSEE provides a copy of a derivative version of the Program to a
+third party, LICENSEE shall provide STANFORD with one copy of the source
+code of the derivative version at no charge to STANFORD.
+
+5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
+By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION
+OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
+THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS,
+COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable
+for any liability nor for any direct, indirect or consequential damages
+with respect to any claim by LICENSEE or any third party on account of or
+arising from this Agreement or use of the Program.
+
+6. This agreement shall be construed, interpreted and applied in
+accordance with the State of California and any legal action arising
+out of this Agreement or use of the Program shall be filed in a court
+in the State of California.
+
+7. Nothing in this Agreement shall be construed as conferring rights to
+use in advertising, publicity or otherwise any trademark or the name
+of ``Stanford''.
+
+The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
diff --git a/usr.sbin/mrouted/Makefile b/usr.sbin/mrouted/Makefile
new file mode 100644
index 0000000..43fe8b1
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= common mrouted mrinfo map-mbone mtrace testrsrr
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/mrouted/Makefile.inc b/usr.sbin/mrouted/Makefile.inc
new file mode 100644
index 0000000..63c2210
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile.inc
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+CFLAGS+= -DRSRR -Dlog=logit
+
+.if exists(${.OBJDIR}/../common)
+LIBMROUTED= ${.OBJDIR}/../common/libmrouted.a
+.else
+LIBMROUTED= ${.CURDIR}/../common/libmrouted.a
+.endif
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/mrouted/RELEASE b/usr.sbin/mrouted/RELEASE
new file mode 100644
index 0000000..9790650
--- /dev/null
+++ b/usr.sbin/mrouted/RELEASE
@@ -0,0 +1,493 @@
+README-3.9-beta3.mrouted,v 1.1.2.1 1998/03/01 03:00:20 fenner Exp
+
+ IP Multicast Extensions for BSD-Derived Unix Systems
+ Multicast Routing Daemon
+
+ Release 3.9-beta3
+ February 28, 1998
+
+ available from parcftp.xerox.com,
+ file pub/net-research/ipmulti/beta-test/mrouted3.9-beta3.tar.Z
+ binaries:
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-sparc-sunos41x.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-sparc-solaris2.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-i386-freebsd22.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-alpha-osf1.tar.Z
+ pub/net-research/ipmulti/beta-test/mrouted3.9-beta3-sgi-irix6.tar.Z
+
+Note: The 3.9 release is mrouted-only, and will run on top of a 3.5 kernel.
+It is a drop-in replacement for mrouted 3.5, 3.6, 3.7 or 3.8.
+
+NOTE WELL: This is a beta-test release of mrouted. The basic
+functionality has been extensively tested in CAIRN and other
+testbeds, but it is expected to have bugs. Please report bugs to Bill
+Fenner <fenner@parc.xerox.com>.
+
+
+The 3.9-beta3 release fixes the following bugs:
+ o There was a bug handling routing updates which caused random black
+ holes.
+
+ o There was a race condition in the timer handlers causing free'd memory
+ to sometimes get touched.
+
+ o "allow_nonpruners" wasn't allowed in the configuration file (and almost
+ nobody noticed! - probably a good sign)
+
+ o When a prune times out and the source has been active "recently",
+ mrouted now waits for further traffic instead of triggering a new
+ prune.
+
+ o mrouted now ignores unreachable routes when making a routing decision
+ (previously it would blackhole, now it can find a less-specific)
+
+The 3.9-beta3 release has the following new features:
+ o A "blaster" keyword for mrouted.conf, to turn on handling of routers
+ (mostly ciscos) which overwhelm the socket buffers by blasting the
+ whole routing table at once.
+
+ o A "notransit" keyword; routes learned on a "notransit" vif will not be
+ readvertised onto another "notransit" vif.
+
+ o The 500kbps default rate limit on tunnels has been removed.
+
+ o An ICMP listener which logs ICMP errors which appear to be in response to
+ tunnel packets that we sent.
+
+ o A tunnel traffic encapsulator, which encapsulates control traffic
+ inside the tunnel instead of unicasting it "beside" the tunnel.
+ This is turned off by default; use "beside off" to turn it on.
+
+ o A "force_leaf" flag to ignore any potential neighbors on a given interface.
+
+
+=========
+3.9-beta2
+June 11, 1997
+
+The 3.9-beta2 release fixes the following bugs:
+ o There was a bug in 3.9-beta1's raw socket buffer processing that
+ would cause an immediate lockup on startup on some systems.
+
+ o RSRR would not clear out the group membership information if
+ further notification of changes to this route entry was not possible.
+
+There is no need to upgrade to 3.9-beta2 if you are not experiencing
+one of the aforementioned bugs.
+
+=========
+3.9-beta1
+June 6, 1997
+
+The 3.9-beta1 release has the following known bugs:
+
+ o The startup message doesn't print properly if you have too many
+ interfaces.
+
+The 3.9-beta1 release fixes the following bugs:
+
+ o mrouted did not properly keep track of subordinates, and would not
+ time out subordinateness. This caused 2 major problems:
+ 1. pruning did not happen when there were equal-cost paths to
+ the same multi-access link
+ 2. subordinateness which did not get cancelled by a non-poisoned
+ route (e.g. in the face of route filtering) did not time out,
+ causing traffic to continue to flow.
+
+ o mrouted's IGMPv2 processing when it is not the querier now
+ conforms to draft-ietf-idmr-igmp-v2-06.txt Thanks to Lorenzo
+ VICISANO <L.Vicisano@cs.ucl.ac.uk> for finding a problem.
+
+ o mrouted is much more careful about forgetting prunes; 3.8
+ would forget prunes whenever any route change ocurred.
+
+The 3.9-beta1 release has the following new features:
+
+ o Longer prune lifetimes (2 hours) by default. Prune lifetimes may
+ be configured per-vif, with the "prune_lifetime N" mrouted.conf
+ configuration file entry (where N is in seconds). This helps to
+ work around the black holes caused on restart when you have a Cisco
+ upstream which does not handle genid's; if this is your situation
+ the recommended value is 300.
+
+ o mrouted's behavior of flooding new routes by default at startup
+ in order to speed healing of paths during startup can be turned off
+ per-vif or globally with the "noflood" configuration option.
+ Turning this option off means you are likely to experience
+ black holes for a minute or two when you restart a router. The
+ default is to flood for a minute or two until mrouted is able to
+ learn subordinate relationships.
+
+ o mrouted now retransmits prunes by default on point-to-point links.
+ prune retransmission can be turned on or off per vif via the
+ "rexmit_prunes [on|off]" mrouted.conf command. Prune retransmission
+ helps on lossy links, and also helps when a router has forgotten
+ about a prune (e.g. if it is out of memory and needs to shed state,
+ or due to a bug).
+
+ o The new "passive" mode causes mrouted to not actively send probes
+ looking for neighbors. This allows a dialup link to become quiescent
+ if there is no DVMRP neighbor on the other end. Configuring
+ "passive" on both ends of a link will cause it to never come up.
+
+ o mrouted defaults to not peering with DVMRP routers that do not
+ prune. Use the "allow_nonpruners" mrouted.conf option on a vif
+ on which you want to allow such peerings.
+
+ o mrouted now allows route filtering. mrouted.conf syntax:
+ accept 13/8
+
+ accepts all routes matching 13/8 (e.g. accepts
+ 13.2.116/22). If you want to accept only exactly
+ 13/8, use
+
+ accept 13/8 exact
+
+ deny 10/8 64/2 130/8 exact 172/8 exact
+
+ denies some common MBone martians
+
+ Only "accept" or "deny" is allowed, no combinations.
+
+ Add "bidir" to apply the filter to output too, otherwise
+ it's input only.
+
+ Expected usage:
+ - Providers filter routes that customers send them
+ - Martian removal
+ - Topology modification (e.g. don't let the existence of
+ private tunnel foo out into the world).
+
+
+ o mrouted now malloc's the buffer it uses for SIOCGIFCONF, to allow
+ for more interfaces. Thanks to Danny Mitzel
+
+ o mrouted now ignores multiple entries for a single interface
+ name (temporary hack until mrouted understands interface aliases)
+
+ o mrouted's "-d" flag has been modified to accept the names of the
+ systems which you would like to debug.
+ packet, prunes, routes, peers, cache, timeout, interface,
+ membership, traceroute, igmp
+
+ o mrouted now times neighbors out fater, and fully detects and
+ ignores routes from one-way peerings.
+
+ o mrouted's route processing has been sped up, especially at startup.
+
+ o mrouted uses the biggest SO_RCVBUF the operating system allows
+ (up to 256Kbytes)
+
+ o mrouted uses TOS 0xc0 ("Internet Control") for DVMRP messages.
+
+===========
+Release 3.8
+November 29, 1995
+
+The 3.8 release fixes the following bugs:
+
+ o mrouted would fail to forget prunes when a neighbor went away,
+ thus potentially sending traffic down a tunnel after the tunnel
+ endpoint has gone down. This was due to some research code making
+ it into the "emergency" 3.7 release, sigh.
+
+ o mrouted could send prunes with negative lifetimes. This causes
+ slightly higher prune traffic but shouldn't be any major problem.
+
+===========
+Release 3.7
+November 28, 1995
+
+The 3.7 release fixes the following bugs:
+
+ o mrouted now ignores route reports that include bogus netmasks.
+ There was a bug in 3.5 that would mangle default routes into
+ tens of bogus routes; this should prevent that bug from killing
+ the MBONE.
+
+ This solution can cause route flaps and black holes until the
+ 3.5's are gone or all of the 3.5's neighbors are 3.7 .
+
+ o mrouted now ignores duplicate routes. Ciscos and the above 3.5
+ bug could cause two copies of the same route to appear in a single
+ routing update; mrouted would insert two copies of the same route
+ into its routing table and wreak all sorts of havoc.
+
+ o mrouted now sends a group-specific query for both retransmissions
+ of a g-s query; previous versions sent a general query the second
+ time.
+
+ o mrouted now loops back multicasted mtrace responses and
+ group-specific membership queries
+
+ o mrouted now performs deterministic tiebreaking between two
+ neighbors on the same vif.
+
+ o mrouted now only does duplicate suppression on traceroute requests,
+ not all traceroute packets, so that a loop can be nicely detected
+ via a duplicate router instead of just a timeout.
+
+ o the buffer size that mrouted uses has been increased to allow
+ more than 16 hops in mtrace messages.
+
+ o mtrace's hop-by-hop termination is now more likely to be correct.
+
+ o mrinfo now waits for the responses to its retransmitted queries.
+
+The 3.7 release has the following new features:
+
+ o The configuration file can accept a hostname as the other end
+ of a tunnel. There must be a single name->ip mapping for the
+ given name, however, or mrouted will fail to start up.
+
+ o mrinfo now sends requests to all interfaces of a multihomed host.
+
+ o mtrace's passive mode has been implemented.
+
+ o The first screen of mtrace statistics is shorter and more likely
+ to fit on one screen.
+
+===========
+Release 3.6
+June 26, 1995
+
+The 3.6 release fixes the following bugs:
+
+ o mrouted would dump core when attempting to report no routes (i.e. upon
+ startup, if you have no enabled phyint's)
+
+ o mrouted would dump core if requested to traceroute a source for which it
+ had no route
+
+ o neighbor flags were not always properly updated on probe or report
+
+ o mrouted would sometimes reply to a multicast traceroute on a disabled
+ phyint; now it uses the first configured phyint to reply to traceroutes.
+
+ o host routes (i.e. netmask 0xffffffff) works now; it was discarding
+ IGMP from the host because it was coming from the "broadcast address"
+ of the subnet.
+
+ o send_igmp() now treats the failure to send an mtrace or a neighbor
+ reply as informational, as opposed to warning.
+
+ o mrouted would go into an infinite loop trying to respond to a traceroute
+ for a source with a netmask of 0xffffffff.
+
+ o vifs_with_neighbors was not being reset if the mrouted was restarted
+ with SIGHUP
+
+ o the default route was not being properly advertised to neighbors (although
+ it was accepted if it was advertised to it)
+
+ o ANSI-fication for those who it helps, still-K&R-ish for those it doesn't.
+
+ o mtrace now attempts to trace three hops past a non-responding router,
+ in the hopes that it does support traceroute but just couldn't respond
+ (i.e. unicast didn't work and it can't source multicast because all its
+ phyints are disabled).
+
+ o mrinfo now times out even on a multicast router.
+
+
+===========
+Release 3.5
+May 8, 1995
+
+The 3.5 release has the following new features:
+
+ o The kernel and mrouted make sure that each is the correct version, to
+ prevent problems with mismatched kernel/mrouted versions. A too-old
+ mrouted will die with the error:
+
+ can't enable DVMRP routing in kernel: Option not supported by protocol
+
+ o mrouted can accept and propogate a default route (essential for
+ heirarchical multicast routing)
+
+ o Kernel route cache keeps source-specific routes instead of subnet routes,
+ eliminating hashing and longest-match problems.
+ (allows classless routing, longest-match and default routing)
+
+ o Cached kernel routes only get deleted if no traffic is flowing, to
+ facilitate multicast traceroute
+
+ o mrouted has a new configuration file parser, which provides better error
+ messages than before, and allows named boundaries (see man page)
+
+ o added "netmask" to phyint configuration, at the suggestion of
+ Anders Klemets
+
+ o System V and FreeBSD compatibility from John Brezak <brezak@ch.hp.com>
+
+ o phyint's can have additional subnets configured, for people with
+ multiple subnets on one physical network. mrouted.conf syntax is
+ altnet 1.2.3.0, or altnet 1.2.3.0/24 if you need to specify
+ a different netmask. There can be as many altnet statements
+ as you need.
+
+ o both mrouted and the kernel now support classless addresses.
+
+ o the kernel supports PIM assert processing by notifying the router
+ when a packet arrives on the wrong interface
+
+ o the kernel keeps additional counters, and mrouted can be compiled to
+ support SNMP and the Multicast MIB
+
+ o the packet classifier in the kernel now uses the following udp port
+ ranges:
+ [0, 16384) - lowest priority, unclassified
+ [16384, 32768) - highest priority, i.e. audio
+ [32768, 49152) - medium priority, i.e. whiteboard
+ [49152, 65536) - low priority, i.e. video
+ A future release of a session directory will allocate ports in these
+ ranges.
+
+ o the configuration code has been modified to default tunnels' rate_limit
+ parameters to 500kbps. This is easily modified with a rate_limit keyword
+ in mrouted.conf, but should be a good default for the MBONE in general.
+
+ o The tunnel sending code now caches a route for ip_output(), this should
+ help performance on machines with lots of tunnels.
+
+ o Dispatching for de-capsulating packets is now via protosw[], making
+ reception of other raw protocols more efficient
+
+ o Neighbor capabilities are discovered via a bitmask as opposed to
+ version number.
+
+ o Multicast traceroute code improved
+
+ o mrouted can be compiled with Routing Support for Resource Reservation
+ (RSRR), required for RSVP.
+
+
+The 3.5 release fixes the following bugs:
+
+ o The IGMPv2 query timeout field was interpreted as being in units of
+ 200ms as opposed to 100ms, thus the maximum timeout was set to twice
+ the expected value. This is not fatal, as mrouted always queries
+ twice in the expectation that a packet could get loss, but it does
+ make it less robust in the face of packet loss.
+
+ o IGMP could report membership in local-only groups (i.e. 224.0.0.X)
+
+ o IGMP could get confused by hearing its own new membership reports, thus
+ a router would never perform fast leave.
+
+ o IGMP could reset timers for the wrong interface.
+
+ o mrouted put a bogus value in the maximum timeout field of IGMPv2 query
+ packets.
+
+ o Non-querier mrouters would respond to IGMP leave messages
+
+ o mrouted was not performing fast leave properly
+
+ o If the last member goes away on a transit network, the upstream router
+ would stop forwarding even if there are downstream members.
+
+ o Kernel hash function improved
+
+ o Eliminated possibility of panic(): timeout in cache maintenance
+
+ o Reordered resource allocation when sending upcall to handle failure properly
+
+ o some endian-ness bugs squashed in mrouted, probably more to go.
+
+ o Multicast traceroute could send a reply on a disabled interface.
+
+
+This release consists of the following files:
+
+
+ README-3.8.mrouted - this file
+
+ mrouted/* - version 3.8 of mrouted,
+ mrinfo, map-mbone and
+ mtrace.
+
+ ifconfig/* - Changes to ifconfig to
+ show multicast interfaces
+
+ netstat/* - Diffs to netstat
+
+ ping/* - sources for ping
+ which support
+ multicasting
+
+ mtest/* - utility for testing
+ multicast group
+ membership
+
+
+MROUTED 3.8
+
+Mrouted 3.8 has two optional features: SNMP and RSRR. RSRR support
+is required for running RSVP; the SNMP code, with the help of the
+ISODE snmpd, implements both the Multicast MIB and the DVMRP MIB.
+
+RSRR
+----
+Routing Support for Resource Reservations (RSRR) was contributed by
+Daniel Zappala <daniel@isi.edu>.
+
+To enable RSRR support, uncomment the three lines starting with
+RSRR near the top of the Makefile and "make clean; make". Or use
+the prebuilt binary, mrouted.rsrr .
+
+RSRR allows RSVP to query mrouted for its routing entry for a particular
+source-group pair. Using the routing entry and the IP_MULTICAST_VIF
+socket call, RSVP can forward distinct control messages out each
+outgoing interface. This version of mrouted supports RSRR messages
+using a Unix datagram socket.
+
+RSRR currently includes two pairs of query-reply messages. RSVP sends
+an Initial Query when it starts. Mrouted responds with an Initial Reply
+that includes the set of vifs it is using, flagging those that are
+administratively disabled. When RSVP needs the routing entry for a
+source-group pair, it sends a Route Query. Mrouted responds with a
+Route Reply that includes the incoming vif and outgoing vifs for the
+source-group pair.
+
+RSVP may request route change notification by setting the notification
+bit in the Route Query. If mrouted can provide route change
+notification for the source-group pair, it sets the notification bit in
+its Route Reply. When the routing entry for the source-group pair
+changes, mrouted sends an unsolicited Route Reply containing the new
+routing information. The initial release of mrouted 3.5 did not support
+route change notification and always returned a Route Reply with the
+notification bit cleared. This release of mrouted provides route change
+notification when possible.
+
+SNMP
+----
+SNMP support was contributed by David Thaler <thalerd@eecs.umich.edu>.
+
+To enable SNMP support, uncomment the six lines near the top of
+the Makefile below the description of SNMP support, or use the
+prebuilt binary, mrouted.snmp or mrouted.rsrr.snmp .
+
+To link the SNMP-capable mrouted, you need the CMU libraries.
+See http://nic.merit.edu/~mbone/ for a full mrouted-snmp distribution.
+
+Make sure to add the "sysName", "sysContact", "sysVersion" and
+"sysLocation" variables to your /etc/mrouted.conf if you want them
+to provide anything other than default values.
+
+Example:
+
+sysName "tibia"
+sysContact "Bill Fenner <fenner@parc.xerox.com> +1 415 812-4816"
+sysVersion "SunOS 4.1.3 and mrouted 3.8"
+sysLocation "MAXC room, PARC building 35"
+
+The SNMP version of mrouted has an additional command line flag:
+
+ -P snmp_port
+
+ Specifies a port for SNMP communication (default 161). This option
+ should be used when another SNMP daemon already exists. The
+ preferred alternate port in this case is port 9161.
+
+The mstat(8) program allows querying of statistics using SNMP.
diff --git a/usr.sbin/mrouted/VERSION b/usr.sbin/mrouted/VERSION
new file mode 100644
index 0000000..a0ba149
--- /dev/null
+++ b/usr.sbin/mrouted/VERSION
@@ -0,0 +1 @@
+3.9-beta3+IOS12
diff --git a/usr.sbin/mrouted/callout.c b/usr.sbin/mrouted/callout.c
new file mode 100644
index 0000000..8b8ea9c
--- /dev/null
+++ b/usr.sbin/mrouted/callout.c
@@ -0,0 +1,250 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * callout.c,v 3.8.4.8 1998/01/06 01:58:45 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/* the code below implements a callout queue */
+static int id = 0;
+static struct timeout_q *Q = 0; /* pointer to the beginning of timeout queue */
+
+struct timeout_q {
+ struct timeout_q *next; /* next event */
+ int id;
+ cfunc_t func; /* function to call */
+ void *data; /* func's data */
+ int time; /* time offset to next event*/
+};
+
+#ifdef IGMP_DEBUG
+static void print_Q __P((void));
+#else
+#define print_Q()
+#endif
+
+void
+callout_init()
+{
+ Q = (struct timeout_q *) 0;
+}
+
+void
+free_all_callouts()
+{
+ struct timeout_q *p;
+
+ while (Q) {
+ p = Q;
+ Q = Q->next;
+ free(p);
+ }
+}
+
+
+/*
+ * elapsed_time seconds have passed; perform all the events that should
+ * happen.
+ */
+void
+age_callout_queue(elapsed_time)
+ int elapsed_time;
+{
+ struct timeout_q *ptr;
+ int i = 0;
+
+ for (ptr = Q; ptr; ptr = Q, i++) {
+ if (ptr->time > elapsed_time) {
+ ptr->time -= elapsed_time;
+ return;
+ } else {
+ elapsed_time -= ptr->time;
+ Q = Q->next;
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "about to call timeout %d (#%d)", ptr->id, i);
+ if (ptr->func)
+ ptr->func(ptr->data);
+ free(ptr);
+ }
+ }
+}
+
+/*
+ * Return in how many seconds age_callout_queue() would like to be called.
+ * Return -1 if there are no events pending.
+ */
+int
+timer_nextTimer()
+{
+ if (Q) {
+ if (Q->time < 0) {
+ log(LOG_WARNING, 0, "timer_nextTimer top of queue says %d",
+ Q->time);
+ return 0;
+ }
+ return Q->time;
+ }
+ return -1;
+}
+
+/*
+ * sets the timer
+ */
+int
+timer_setTimer(delay, action, data)
+ int delay; /* number of units for timeout */
+ cfunc_t action; /* function to be called on timeout */
+ void *data; /* what to call the timeout function with */
+{
+ struct timeout_q *ptr, *node, *prev;
+ int i = 0;
+
+ /* create a node */
+ node = (struct timeout_q *)malloc(sizeof(struct timeout_q));
+ if (node == 0) {
+ log(LOG_WARNING, 0, "Malloc Failed in timer_settimer\n");
+ return -1;
+ }
+ node->func = action;
+ node->data = data;
+ node->time = delay;
+ node->next = 0;
+ node->id = ++id;
+
+ prev = ptr = Q;
+
+ /* insert node in the queue */
+
+ /* if the queue is empty, insert the node and return */
+ if (!Q)
+ Q = node;
+ else {
+ /* chase the pointer looking for the right place */
+ while (ptr) {
+
+ if (delay < ptr->time) {
+ /* right place */
+
+ node->next = ptr;
+ if (ptr == Q)
+ Q = node;
+ else
+ prev->next = node;
+ ptr->time -= node->time;
+ print_Q();
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "created timeout %d (#%d)", node->id, i);
+ return node->id;
+ } else {
+ /* keep moving */
+
+ delay -= ptr->time; node->time = delay;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ i++;
+ }
+ prev->next = node;
+ }
+ print_Q();
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "created timeout %d (#%d)", node->id, i);
+ return node->id;
+}
+
+/* returns the time until the timer is scheduled */
+int
+timer_leftTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr;
+ int left = 0;
+
+ if (!timer_id)
+ return -1;
+
+ for (ptr = Q; ptr; ptr = ptr->next) {
+ left += ptr->time;
+ if (ptr->id == timer_id)
+ return left;
+ }
+ return -1;
+}
+
+/* clears the associated timer. Returns 1 if succeeded. */
+int
+timer_clearTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr, *prev;
+ int i = 0;
+
+ if (!timer_id)
+ return 0;
+
+ prev = ptr = Q;
+
+ /*
+ * find the right node, delete it. the subsequent node's time
+ * gets bumped up
+ */
+
+ print_Q();
+ while (ptr) {
+ if (ptr->id == timer_id) {
+ /* got the right node */
+
+ /* unlink it from the queue */
+ if (ptr == Q)
+ Q = Q->next;
+ else
+ prev->next = ptr->next;
+
+ /* increment next node if any */
+ if (ptr->next != 0)
+ (ptr->next)->time += ptr->time;
+
+ if (ptr->data)
+ free(ptr->data);
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "deleted timer %d (#%d)", ptr->id, i);
+ free(ptr);
+ print_Q();
+ return 1;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ i++;
+ }
+ IF_DEBUG(DEBUG_TIMEOUT)
+ log(LOG_DEBUG, 0, "failed to delete timer %d (#%d)", timer_id, i);
+ print_Q();
+ return 0;
+}
+
+#ifdef IGMP_DEBUG
+/*
+ * debugging utility
+ */
+static void
+print_Q()
+{
+ struct timeout_q *ptr;
+
+ IF_DEBUG(DEBUG_TIMEOUT)
+ for (ptr = Q; ptr; ptr = ptr->next)
+ log(LOG_DEBUG, 0, "(%d,%d) ", ptr->id, ptr->time);
+}
+#endif /* IGMP_DEBUG */
diff --git a/usr.sbin/mrouted/cfparse.y b/usr.sbin/mrouted/cfparse.y
new file mode 100644
index 0000000..1827f6a
--- /dev/null
+++ b/usr.sbin/mrouted/cfparse.y
@@ -0,0 +1,932 @@
+%{
+/*
+ * Configuration file parser for mrouted.
+ *
+ * Written by Bill Fenner, NRL, 1994
+ *
+ * $FreeBSD$
+ * cfparse.y,v 3.8.4.30 1998/03/01 01:48:58 fenner Exp
+ */
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "defs.h"
+#include <netdb.h>
+
+/*
+ * Local function declarations
+ */
+static void fatal __P((char *fmt, ...)) __printflike(1, 2);
+static void warn __P((char *fmt, ...)) __printflike(1, 2);
+static void yyerror __P((char *s));
+static char * next_word __P((void));
+static int yylex __P((void));
+static u_int32 valid_if __P((char *s));
+static struct ifreq * ifconfaddr __P((struct ifconf *ifcp, u_int32 a));
+int yyparse __P((void));
+
+static FILE *f;
+
+char *configfilename = _PATH_MROUTED_CONF;
+
+extern int cache_lifetime;
+extern int prune_lifetime;
+
+/* imported from config.c, with slight memory leak */
+extern struct ifconf ifc;
+
+int allow_black_holes = 0;
+
+static int lineno;
+
+static struct uvif *v;
+
+static int order, state;
+static int noflood = 0;
+static int rexmit = VIFF_REXMIT_PRUNES;
+
+struct addrmask {
+ u_int32 addr;
+ int mask;
+};
+
+struct boundnam {
+ char *name;
+ struct addrmask bound;
+};
+
+#define MAXBOUNDS 20
+
+struct boundnam boundlist[MAXBOUNDS]; /* Max. of 20 named boundaries */
+int numbounds = 0; /* Number of named boundaries */
+
+%}
+
+%union
+{
+ int num;
+ char *ptr;
+ struct addrmask addrmask;
+ u_int32 addr;
+ struct vf_element *filterelem;
+};
+
+%token CACHE_LIFETIME PRUNE_LIFETIME PRUNING BLACK_HOLE NOFLOOD
+%token PHYINT TUNNEL NAME
+%token DISABLE IGMPV1 SRCRT BESIDE
+%token METRIC THRESHOLD RATE_LIMIT BOUNDARY NETMASK ALTNET ADVERT_METRIC
+%token FILTER ACCEPT DENY EXACT BIDIR REXMIT_PRUNES REXMIT_PRUNES2
+%token PASSIVE ALLOW_NONPRUNERS
+%token NOTRANSIT BLASTER FORCE_LEAF
+%token PRUNE_LIFETIME2 NOFLOOD2
+%token SYSNAM SYSCONTACT SYSVERSION SYSLOCATION
+%token <num> BOOLEAN
+%token <num> NUMBER
+%token <ptr> STRING
+%token <addrmask> ADDRMASK
+%token <addr> ADDR
+
+%type <addr> interface addrname
+%type <addrmask> bound boundary addrmask
+%type <filterelem> filter filtlist filtelement filtelem
+
+%start conf
+
+%%
+
+conf : stmts
+ ;
+
+stmts : /* Empty */
+ | stmts stmt
+ ;
+
+stmt : error
+ | PHYINT interface {
+
+ vifi_t vifi;
+
+ state++;
+
+ if (order)
+ fatal("phyints must appear before tunnels");
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ if (!(v->uv_flags & VIFF_TUNNEL) &&
+ $2 == v->uv_lcl_addr)
+ break;
+
+ if (vifi == numvifs)
+ fatal("%s is not a configured interface",
+ inet_fmt($2,s1));
+
+ }
+ ifmods
+ | TUNNEL interface addrname {
+
+ struct ifreq *ifr;
+ struct ifreq ffr;
+ vifi_t vifi;
+
+ order++;
+
+ ifr = ifconfaddr(&ifc, $2);
+ if (ifr == 0)
+ fatal("Tunnel local address %s is not mine",
+ inet_fmt($2, s1));
+
+ if (((ntohl($2) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT) ==
+ IN_LOOPBACKNET)
+ fatal("Tunnel local address %s is a loopback address",
+ inet_fmt($2, s1));
+
+ if (ifconfaddr(&ifc, $3) != 0)
+ fatal("Tunnel remote address %s is one of mine",
+ inet_fmt($3, s1));
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ if (v->uv_flags & VIFF_TUNNEL) {
+ if ($3 == v->uv_rmt_addr)
+ fatal("Duplicate tunnel to %s",
+ inet_fmt($3, s1));
+ } else if (!(v->uv_flags & VIFF_DISABLED)) {
+ if (($3 & v->uv_subnetmask) == v->uv_subnet)
+ fatal("Unnecessary tunnel to %s, same subnet as vif %d (%s)",
+ inet_fmt($3,s1), vifi, v->uv_name);
+ }
+
+ if (numvifs == MAXVIFS)
+ fatal("too many vifs");
+
+ strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr)<0)
+ fatal("ioctl SIOCGIFFLAGS on %s", ffr.ifr_name);
+
+ v = &uvifs[numvifs];
+ zero_vif(v, 1);
+ v->uv_flags = VIFF_TUNNEL | rexmit | noflood;
+ v->uv_flags |= VIFF_OTUNNEL; /*XXX*/
+ v->uv_lcl_addr = $2;
+ v->uv_rmt_addr = $3;
+ v->uv_dst_addr = $3;
+ strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
+ v->uv_name[IFNAMSIZ-1]='\0';
+
+ if (!(ffr.ifr_flags & IFF_UP)) {
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+ }
+ tunnelmods
+ {
+
+ if (!(v->uv_flags & VIFF_OTUNNEL)) {
+ init_ipip_on_vif(v);
+ }
+
+ log(LOG_INFO, 0,
+ "installing tunnel from %s to %s as vif #%u - rate=%d",
+ inet_fmt($2, s1), inet_fmt($3, s2),
+ numvifs, v->uv_rate_limit);
+
+ ++numvifs;
+
+ }
+ | CACHE_LIFETIME NUMBER {
+
+ if ($2 < MIN_CACHE_LIFETIME) {
+ warn("cache_lifetime %d must be at least %d",
+ $2, MIN_CACHE_LIFETIME);
+ } else {
+ cache_lifetime = $2;
+ }
+
+ }
+ | PRUNE_LIFETIME NUMBER {
+
+ if ($2 < MIN_PRUNE_LIFETIME) {
+ warn("prune_lifetime %d must be at least %d",
+ $2, MIN_PRUNE_LIFETIME);
+ } else {
+ prune_lifetime = $2;
+ }
+
+ }
+ | PRUNING BOOLEAN {
+
+ if ($2 != 1) {
+ warn("Disabling pruning is no longer supported");
+ }
+
+ }
+ | BLACK_HOLE {
+#ifdef ALLOW_BLACK_HOLES
+ allow_black_holes = 1;
+#endif
+ }
+ /*
+ * Turn off initial flooding (until subordinateness is learned
+ * via route exchange) on all phyints and set the default for
+ * all further tunnels.
+ */
+ | NOFLOOD {
+
+ vifi_t vifi;
+
+ noflood = VIFF_NOFLOOD;
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_NOFLOOD;
+
+ }
+ /*
+ * Turn on prune retransmission on all interfaces.
+ * Tunnels default to retransmitting, so this just
+ * needs to turn on phyints.
+ */
+ | REXMIT_PRUNES {
+
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+
+ }
+ /*
+ * If true, do as above. If false, no need to turn
+ * it off for phyints since they default to not
+ * rexmit; need to set flag to not rexmit on tunnels.
+ */
+ | REXMIT_PRUNES BOOLEAN {
+
+ if ($2) {
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs;
+ vifi < numvifs;
+ ++vifi, ++v)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+ } else {
+ rexmit = 0;
+ }
+
+ }
+ | NAME STRING boundary { if (numbounds >= MAXBOUNDS) {
+ fatal("Too many named boundaries (max %d)", MAXBOUNDS);
+ }
+
+ boundlist[numbounds].name = malloc(strlen($2) + 1);
+ strcpy(boundlist[numbounds].name, $2);
+ boundlist[numbounds++].bound = $3;
+ }
+ | SYSNAM STRING {
+#ifdef SNMP
+ set_sysName($2);
+#endif /* SNMP */
+ }
+ | SYSCONTACT STRING {
+#ifdef SNMP
+ set_sysContact($2);
+#endif /* SNMP */
+ }
+ | SYSVERSION STRING {
+#ifdef SNMP
+ set_sysVersion($2);
+#endif /* SNMP */
+ }
+ | SYSLOCATION STRING {
+#ifdef SNMP
+ set_sysLocation($2);
+#endif /* SNMP */
+ }
+ ;
+
+tunnelmods : /* empty */
+ | tunnelmods tunnelmod
+ ;
+
+tunnelmod : mod
+ | BESIDE { v->uv_flags |= VIFF_OTUNNEL; }
+ | BESIDE BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_OTUNNEL;
+ } else {
+ v->uv_flags &= ~VIFF_OTUNNEL;
+ }
+
+ }
+ | SRCRT { fatal("Source-route tunnels not supported"); }
+ ;
+
+ifmods : /* empty */
+ | ifmods ifmod
+ ;
+
+ifmod : mod
+ | DISABLE { v->uv_flags |= VIFF_DISABLED; }
+ | IGMPV1 { v->uv_flags |= VIFF_IGMPV1; }
+ | NETMASK addrname {
+ u_int32 subnet, mask;
+
+ mask = $2;
+ subnet = v->uv_lcl_addr & mask;
+ if (!inet_valid_subnet(subnet, mask))
+ fatal("Invalid netmask");
+ v->uv_subnet = subnet;
+ v->uv_subnetmask = mask;
+ v->uv_subnetbcast = subnet | ~mask;
+ }
+ | NETMASK {
+
+ warn("Expected address after netmask keyword, ignored");
+
+ }
+ | ALTNET addrmask {
+
+ struct phaddr *ph;
+
+ ph = (struct phaddr *)malloc(sizeof(struct phaddr));
+ if (ph == NULL)
+ fatal("out of memory");
+ if ($2.mask) {
+ VAL_TO_MASK(ph->pa_subnetmask, $2.mask);
+ } else
+ ph->pa_subnetmask = v->uv_subnetmask;
+ ph->pa_subnet = $2.addr & ph->pa_subnetmask;
+ ph->pa_subnetbcast = ph->pa_subnet | ~ph->pa_subnetmask;
+ if ($2.addr & ~ph->pa_subnetmask)
+ warn("Extra subnet %s/%d has host bits set",
+ inet_fmt($2.addr,s1), $2.mask);
+ ph->pa_next = v->uv_addrs;
+ v->uv_addrs = ph;
+
+ }
+ | ALTNET {
+
+ warn("Expected address after altnet keyword, ignored");
+
+ }
+ | FORCE_LEAF {
+
+ v->uv_flags |= VIFF_FORCE_LEAF;
+
+ }
+ | FORCE_LEAF BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_FORCE_LEAF;
+ } else {
+ v->uv_flags &= ~VIFF_FORCE_LEAF;
+ }
+
+ }
+ ;
+
+mod : THRESHOLD NUMBER { if ($2 < 1 || $2 > 255)
+ fatal("Invalid threshold %d",$2);
+ v->uv_threshold = $2;
+ }
+ | THRESHOLD {
+
+ warn("Expected number after threshold keyword, ignored");
+
+ }
+ | METRIC NUMBER { if ($2 < 1 || $2 > UNREACHABLE)
+ fatal("Invalid metric %d",$2);
+ v->uv_metric = $2;
+ }
+ | METRIC {
+
+ warn("Expected number after metric keyword, ignored");
+
+ }
+ | ADVERT_METRIC NUMBER { if ($2 < 0 || $2 > UNREACHABLE - 1)
+ fatal("Invalid advert_metric %d", $2);
+ v->uv_admetric = $2;
+ }
+ | ADVERT_METRIC {
+
+ warn("Expected number after advert_metric keyword, ignored");
+
+ }
+ | RATE_LIMIT NUMBER { if ($2 > MAX_RATE_LIMIT)
+ fatal("Invalid rate_limit %d",$2);
+ v->uv_rate_limit = $2;
+ }
+ | RATE_LIMIT {
+
+ warn("Expected number after rate_limit keyword, ignored");
+
+ }
+ | BOUNDARY bound {
+
+ struct vif_acl *v_acl;
+
+ v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
+ if (v_acl == NULL)
+ fatal("out of memory");
+ VAL_TO_MASK(v_acl->acl_mask, $2.mask);
+ v_acl->acl_addr = $2.addr & v_acl->acl_mask;
+ if ($2.addr & ~v_acl->acl_mask)
+ warn("Boundary spec %s/%d has host bits set",
+ inet_fmt($2.addr,s1),$2.mask);
+ v_acl->acl_next = v->uv_acl;
+ v->uv_acl = v_acl;
+
+ }
+ | BOUNDARY {
+
+ warn("Expected boundary spec after boundary keyword, ignored");
+
+ }
+ | REXMIT_PRUNES2 {
+
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+
+ }
+ | REXMIT_PRUNES2 BOOLEAN {
+
+ if ($2) {
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+ } else {
+ v->uv_flags &= ~VIFF_REXMIT_PRUNES;
+ }
+
+ }
+ | PASSIVE {
+
+ v->uv_flags |= VIFF_PASSIVE;
+
+ }
+ | NOFLOOD2 {
+
+ v->uv_flags |= VIFF_NOFLOOD;
+
+ }
+ | NOTRANSIT {
+
+ v->uv_flags |= VIFF_NOTRANSIT;
+
+ }
+ | BLASTER {
+
+ v->uv_flags |= VIFF_BLASTER;
+ blaster_alloc(v - uvifs);
+
+ }
+ | ALLOW_NONPRUNERS {
+
+ v->uv_flags |= VIFF_ALLOW_NONPRUNERS;
+
+ }
+ | PRUNE_LIFETIME2 NUMBER {
+
+ if ($2 < MIN_PRUNE_LIFETIME) {
+ warn("prune_lifetime %d must be at least %d",
+ $2, MIN_PRUNE_LIFETIME);
+ } else {
+ v->uv_prune_lifetime = $2;
+ }
+
+ }
+ | ACCEPT filter {
+
+ if (v->uv_filter == NULL) {
+ struct vif_filter *v_filter;
+
+ v_filter = (struct vif_filter *)malloc(sizeof(struct vif_filter));
+ if (v_filter == NULL)
+ fatal("out of memory");
+ v_filter->vf_flags = 0;
+ v_filter->vf_type = VFT_ACCEPT;
+ v_filter->vf_filter = $2;
+ v->uv_filter = v_filter;
+ } else if (v->uv_filter->vf_type != VFT_ACCEPT) {
+ fatal("can't accept and deny");
+ } else {
+ struct vf_element *p;
+
+ p = v->uv_filter->vf_filter;
+ while (p->vfe_next)
+ p = p->vfe_next;
+ p->vfe_next = $2;
+ }
+
+ }
+ | ACCEPT {
+
+ warn("Expected filter spec after accept keyword, ignored");
+
+ }
+ | DENY filter {
+
+ if (v->uv_filter == NULL) {
+ struct vif_filter *v_filter;
+
+ v_filter = (struct vif_filter *)malloc(sizeof(struct vif_filter));
+ if (v_filter == NULL)
+ fatal("out of memory");
+ v_filter->vf_flags = 0;
+ v_filter->vf_type = VFT_DENY;
+ v_filter->vf_filter = $2;
+ v->uv_filter = v_filter;
+ } else if (v->uv_filter->vf_type != VFT_DENY) {
+ fatal("can't accept and deny");
+ } else {
+ struct vf_element *p;
+
+ p = v->uv_filter->vf_filter;
+ while (p->vfe_next)
+ p = p->vfe_next;
+ p->vfe_next = $2;
+ }
+
+ }
+ | DENY {
+
+ warn("Expected filter spec after deny keyword, ignored");
+
+ }
+ | BIDIR {
+
+ if (v->uv_filter == NULL) {
+ fatal("bidir goes after filters");
+ }
+ v->uv_filter->vf_flags |= VFF_BIDIR;
+
+ }
+ ;
+
+interface : ADDR { $$ = $1; }
+ | STRING {
+ $$ = valid_if($1);
+ if ($$ == 0)
+ fatal("Invalid interface name %s",$1);
+ }
+ ;
+
+addrname : ADDR { $$ = $1; }
+ | STRING { struct hostent *hp;
+
+ if ((hp = gethostbyname($1)) == NULL ||
+ hp->h_length != sizeof($$))
+ fatal("No such host %s", $1);
+
+ if (hp->h_addr_list[1])
+ fatal("Hostname %s does not %s",
+ $1, "map to a unique address");
+
+ bcopy(hp->h_addr_list[0], &$$,
+ hp->h_length);
+ }
+
+bound : boundary { $$ = $1; }
+ | STRING { int i;
+
+ for (i=0; i < numbounds; i++) {
+ if (!strcmp(boundlist[i].name, $1)) {
+ $$ = boundlist[i].bound;
+ break;
+ }
+ }
+ if (i == numbounds) {
+ fatal("Invalid boundary name %s",$1);
+ }
+ }
+ ;
+
+boundary : ADDRMASK {
+
+#ifdef ALLOW_BLACK_HOLES
+ if (!allow_black_holes)
+#endif
+ if ((ntohl($1.addr) & 0xff000000) != 0xef000000) {
+ fatal("Boundaries must be 239.x.x.x, not %s/%d",
+ inet_fmt($1.addr, s1), $1.mask);
+ }
+ $$ = $1;
+
+ }
+ ;
+
+addrmask : ADDRMASK { $$ = $1; }
+ | ADDR { $$.addr = $1; $$.mask = 0; }
+ ;
+
+filter : filtlist { $$ = $1; }
+ | STRING { fatal("named filters no implemented yet"); }
+ ;
+
+filtlist : filtelement { $$ = $1; }
+ | filtelement filtlist { $1->vfe_next = $2; $$ = $1; }
+ ;
+
+filtelement : filtelem { $$ = $1; }
+ | filtelem EXACT { $1->vfe_flags |= VFEF_EXACT; $$ = $1; }
+ ;
+
+filtelem : ADDRMASK {
+
+ struct vf_element *vfe;
+
+ vfe = (struct vf_element *)malloc(sizeof(struct vf_element));
+ if (vfe == NULL)
+ fatal("out of memory");
+
+ vfe->vfe_addr = $1.addr;
+ VAL_TO_MASK(vfe->vfe_mask, $1.mask);
+ vfe->vfe_flags = 0;
+ vfe->vfe_next = NULL;
+
+ $$ = vfe;
+
+ }
+%%
+#ifdef __STDC__
+static void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+ char buf[MAXHOSTNAMELEN + 100];
+
+ va_start(ap, fmt);
+#else
+/*VARARGS1*/
+static void
+fatal(fmt, va_alist)
+char *fmt;
+va_dcl
+{
+ va_list ap;
+ char buf[MAXHOSTNAMELEN + 100];
+
+ va_start(ap);
+#endif
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ log(LOG_ERR,0,"%s: %s near line %d", configfilename, buf, lineno);
+}
+
+#ifdef __STDC__
+static void
+warn(char *fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap, fmt);
+#else
+/*VARARGS1*/
+static void
+warn(fmt, va_alist)
+char *fmt;
+va_dcl
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap);
+#endif
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ log(LOG_WARNING,0,"%s: %s near line %d", configfilename, buf, lineno);
+}
+
+static void
+yyerror(s)
+char *s;
+{
+ log(LOG_ERR, 0, "%s: %s near line %d", configfilename, s, lineno);
+}
+
+static char *
+next_word()
+{
+ static char buf[1024];
+ static char *p=NULL;
+ char *q;
+
+ while (1) {
+ if (!p || !*p) {
+ lineno++;
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ return NULL;
+ p = buf;
+ }
+ while (*p && (*p == ' ' || *p == '\t')) /* skip whitespace */
+ p++;
+ if (*p == '#') {
+ p = NULL; /* skip comments */
+ continue;
+ }
+ q = p;
+#ifdef SNMP
+ if (*p == '"') {
+ p++;
+ while (*p && *p != '"' && *p != '\n')
+ p++; /* find next whitespace */
+ if (*p == '"')
+ p++;
+ } else
+#endif
+ while (*p && *p != ' ' && *p != '\t' && *p != '\n')
+ p++; /* find next whitespace */
+ *p++ = '\0'; /* null-terminate string */
+
+ if (!*q) {
+ p = NULL;
+ continue; /* if 0-length string, read another line */
+ }
+
+ return q;
+ }
+}
+
+/*
+ * List of keywords. Must have an empty record at the end to terminate
+ * list. If a second value is specified, the first is used at the beginning
+ * of the file and the second is used while parsing interfaces (e.g. after
+ * the first "phyint" or "tunnel" keyword).
+ */
+static struct keyword {
+ char *word;
+ int val1;
+ int val2;
+} words[] = {
+ { "cache_lifetime", CACHE_LIFETIME },
+ { "prune_lifetime", PRUNE_LIFETIME, PRUNE_LIFETIME2 },
+ { "pruning", PRUNING },
+ { "phyint", PHYINT },
+ { "tunnel", TUNNEL },
+ { "disable", DISABLE },
+ { "metric", METRIC },
+ { "advert_metric", ADVERT_METRIC },
+ { "threshold", THRESHOLD },
+ { "rate_limit", RATE_LIMIT },
+ { "force_leaf", FORCE_LEAF },
+ { "srcrt", SRCRT },
+ { "sourceroute", SRCRT },
+ { "boundary", BOUNDARY },
+ { "netmask", NETMASK },
+ { "igmpv1", IGMPV1 },
+ { "altnet", ALTNET },
+ { "name", NAME },
+ { "accept", ACCEPT },
+ { "deny", DENY },
+ { "exact", EXACT },
+ { "bidir", BIDIR },
+ { "allow_nonpruners", ALLOW_NONPRUNERS },
+#ifdef ALLOW_BLACK_HOLES
+ { "allow_black_holes", BLACK_HOLE },
+#endif
+ { "noflood", NOFLOOD, NOFLOOD2},
+ { "notransit", NOTRANSIT },
+ { "blaster", BLASTER },
+ { "rexmit_prunes", REXMIT_PRUNES, REXMIT_PRUNES2 },
+ { "passive", PASSIVE },
+ { "beside", BESIDE },
+#ifdef SNMP
+ { "sysName", SYSNAM },
+ { "sysContact", SYSCONTACT },
+ { "sysVersion", SYSVERSION },
+ { "sysLocation", SYSLOCATION },
+#endif
+ { NULL, 0 }
+};
+
+
+static int
+yylex()
+{
+ int n;
+ u_int32 addr;
+ char *q;
+ struct keyword *w;
+
+ if ((q = next_word()) == NULL) {
+ return 0;
+ }
+
+ for (w = words; w->word; w++)
+ if (!strcmp(q, w->word))
+ return (state && w->val2) ? w->val2 : w->val1;
+
+ if (!strcmp(q,"on") || !strcmp(q,"yes")) {
+ yylval.num = 1;
+ return BOOLEAN;
+ }
+ if (!strcmp(q,"off") || !strcmp(q,"no")) {
+ yylval.num = 0;
+ return BOOLEAN;
+ }
+ if (!strcmp(q,"default")) {
+ yylval.addrmask.mask = 0;
+ yylval.addrmask.addr = 0;
+ return ADDRMASK;
+ }
+ if (sscanf(q,"%[.0-9]/%d%c",s1,&n,s2) == 2) {
+ if ((addr = inet_parse(s1,1)) != 0xffffffff) {
+ yylval.addrmask.mask = n;
+ yylval.addrmask.addr = addr;
+ return ADDRMASK;
+ }
+ /* fall through to returning STRING */
+ }
+ if (sscanf(q,"%[.0-9]%c",s1,s2) == 1) {
+ if ((addr = inet_parse(s1,4)) != 0xffffffff &&
+ inet_valid_host(addr)) {
+ yylval.addr = addr;
+ return ADDR;
+ }
+ }
+ if (sscanf(q,"0x%8x%c",&n,s1) == 1) {
+ yylval.addr = n;
+ return ADDR;
+ }
+ if (sscanf(q,"%d%c",&n,s1) == 1) {
+ yylval.num = n;
+ return NUMBER;
+ }
+#ifdef SNMP
+ if (*q=='"') {
+ if (q[ strlen(q)-1 ]=='"')
+ q[ strlen(q)-1 ]='\0'; /* trash trailing quote */
+ yylval.ptr = q+1;
+ return STRING;
+ }
+#endif
+ yylval.ptr = q;
+ return STRING;
+}
+
+void
+config_vifs_from_file()
+{
+ order = 0;
+ state = 0;
+ numbounds = 0;
+ lineno = 0;
+
+ if ((f = fopen(configfilename, "r")) == NULL) {
+ if (errno != ENOENT)
+ log(LOG_ERR, errno, "can't open %s", configfilename);
+ return;
+ }
+
+ yyparse();
+
+ fclose(f);
+}
+
+static u_int32
+valid_if(s)
+char *s;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ for (vifi=0, v=uvifs; vifi<numvifs; vifi++, v++)
+ if (!strcmp(v->uv_name, s))
+ return v->uv_lcl_addr;
+
+ return 0;
+}
+
+static struct ifreq *
+ifconfaddr(ifcp, a)
+ struct ifconf *ifcp;
+ u_int32 a;
+{
+ int n;
+ struct ifreq *ifrp = (struct ifreq *)ifcp->ifc_buf;
+ struct ifreq *ifend = (struct ifreq *)((char *)ifrp + ifcp->ifc_len);
+
+ while (ifrp < ifend) {
+ if (ifrp->ifr_addr.sa_family == AF_INET &&
+ ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr == a)
+ return (ifrp);
+#ifdef HAVE_SA_LEN
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ ++ifrp;
+ else
+ ifrp = (struct ifreq *)((char *)ifrp + n);
+#else
+ ++ifrp;
+#endif
+ }
+ return (0);
+}
diff --git a/usr.sbin/mrouted/common/Makefile b/usr.sbin/mrouted/common/Makefile
new file mode 100644
index 0000000..4a286c6
--- /dev/null
+++ b/usr.sbin/mrouted/common/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+LIB= mrouted
+INTERNALLIB= YES
+SRCS= igmp.c inet.c kern.c
+
+CFLAGS+= -I$S
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c
new file mode 100644
index 0000000..9ca6f88
--- /dev/null
+++ b/usr.sbin/mrouted/config.c
@@ -0,0 +1,175 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * config.c,v 3.8.4.10 1998/01/06 01:57:41 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+
+struct ifconf ifc;
+
+/*
+ * Query the kernel to find network interfaces that are multicast-capable
+ * and install them in the uvifs array.
+ */
+void
+config_vifs_from_kernel()
+{
+ struct ifreq *ifrp, *ifend;
+ register struct uvif *v;
+ register vifi_t vifi;
+ int n;
+ u_int32 addr, mask, subnet;
+ int flags;
+ int num_ifreq = 32;
+
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = malloc(ifc.ifc_len);
+ while (ifc.ifc_buf) {
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ /*
+ * If the buffer was large enough to hold all the addresses
+ * then break out, otherwise increase the buffer size and
+ * try again.
+ *
+ * The only way to know that we definitely had enough space
+ * is to know that there was enough space for at least one
+ * more struct ifreq. ???
+ */
+ if ((num_ifreq * sizeof(struct ifreq)) >=
+ ifc.ifc_len + sizeof(struct ifreq))
+ break;
+
+ num_ifreq *= 2;
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len);
+ }
+ if (ifc.ifc_buf == NULL)
+ log(LOG_ERR, 0, "config_vifs_from_kernel: ran out of memory");
+
+ ifrp = (struct ifreq *)ifc.ifc_buf;
+ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
+ /*
+ * Loop through all of the interfaces.
+ */
+ for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
+ struct ifreq ifr;
+#ifdef HAVE_SA_LEN
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+#else
+ n = sizeof(*ifrp);
+#endif
+ /*
+ * Ignore any interface for an address family other than IP.
+ */
+ if (ifrp->ifr_addr.sa_family != AF_INET)
+ continue;
+
+ addr = ((struct sockaddr_in *)&ifrp->ifr_addr)->sin_addr.s_addr;
+
+ /*
+ * Need a template to preserve address info that is
+ * used below to locate the next entry. (Otherwise,
+ * SIOCGIFFLAGS stomps over it because the requests
+ * are returned in a union.)
+ */
+ bcopy(ifrp->ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
+
+ /*
+ * Ignore loopback interfaces and interfaces that do not support
+ * multicast.
+ */
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+ flags = ifr.ifr_flags;
+ if ((flags & (IFF_LOOPBACK|IFF_MULTICAST)) != IFF_MULTICAST) continue;
+
+ /*
+ * Ignore any interface whose address and mask do not define a
+ * valid subnet number, or whose address is of the form {subnet,0}
+ * or {subnet,-1}.
+ */
+ if (ioctl(udp_socket, SIOCGIFNETMASK, (char *)&ifr) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFNETMASK for %s", ifr.ifr_name);
+ mask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+ subnet = addr & mask;
+ if (!inet_valid_subnet(subnet, mask) ||
+ addr == subnet ||
+ addr == (subnet | ~mask)) {
+ log(LOG_WARNING, 0,
+ "ignoring %s, has invalid address (%s) and/or mask (%s)",
+ ifr.ifr_name, inet_fmt(addr, s1), inet_fmt(mask, s2));
+ continue;
+ }
+
+ /*
+ * Ignore any interface that is connected to the same subnet as
+ * one already installed in the uvifs array.
+ */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (strcmp(v->uv_name, ifr.ifr_name) == 0) {
+ log(LOG_DEBUG, 0, "skipping %s (%s on subnet %s) (alias for vif#%u?)",
+ v->uv_name, inet_fmt(addr, s1),
+ inet_fmts(subnet, mask, s2), vifi);
+ break;
+ }
+ if ((addr & v->uv_subnetmask) == v->uv_subnet ||
+ (v->uv_subnet & mask) == subnet) {
+ log(LOG_WARNING, 0, "ignoring %s, same subnet as %s",
+ ifr.ifr_name, v->uv_name);
+ break;
+ }
+ }
+ if (vifi != numvifs) continue;
+
+ /*
+ * If there is room in the uvifs array, install this interface.
+ */
+ if (numvifs == MAXVIFS) {
+ log(LOG_WARNING, 0, "too many vifs, ignoring %s", ifr.ifr_name);
+ continue;
+ }
+ v = &uvifs[numvifs];
+ zero_vif(v, 0);
+ v->uv_lcl_addr = addr;
+ v->uv_subnet = subnet;
+ v->uv_subnetmask = mask;
+ v->uv_subnetbcast = subnet | ~mask;
+ strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
+ v->uv_name[IFNAMSIZ-1] = '\0';
+
+ if (flags & IFF_POINTOPOINT)
+ v->uv_flags |= VIFF_REXMIT_PRUNES;
+
+ log(LOG_INFO,0,"installing %s (%s on subnet %s) as vif #%u - rate=%d",
+ v->uv_name, inet_fmt(addr, s1), inet_fmts(subnet, mask, s2),
+ numvifs, v->uv_rate_limit);
+
+ ++numvifs;
+
+ /*
+ * If the interface is not yet up, set the vifs_down flag to
+ * remind us to check again later.
+ */
+ if (!(flags & IFF_UP)) {
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+ }
+}
diff --git a/usr.sbin/mrouted/defs.h b/usr.sbin/mrouted/defs.h
new file mode 100644
index 0000000..267c158
--- /dev/null
+++ b/usr.sbin/mrouted/defs.h
@@ -0,0 +1,413 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * defs.h,v 3.8.4.15 1998/03/01 02:51:42 fenner Exp
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#ifdef SYSV
+#include <sys/sockio.h>
+#endif
+#ifdef _AIX
+#include <time.h>
+#endif
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <net/if.h>
+#define rtentry kern_rtentry /* XXX !!! UGH */
+#include <net/route.h>
+#undef rtentry
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/igmp.h>
+#ifdef __FreeBSD__ /* sigh */
+#include <osreldate.h>
+#if __FreeBSD_version >= 220000
+#define rtentry kernel_rtentry
+#include <net/route.h>
+#undef rtentry
+#endif
+#endif
+#include <netinet/ip_mroute.h>
+#ifdef RSRR
+#include <sys/un.h>
+#endif /* RSRR */
+
+/*XXX*/
+typedef u_int u_int32;
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+typedef void (*cfunc_t) __P((void *));
+typedef void (*ihfunc_t) __P((int, fd_set *));
+
+#include "dvmrp.h"
+#include "igmpv2.h"
+#include "vif.h"
+#include "route.h"
+#include "prune.h"
+#include "pathnames.h"
+#ifdef RSRR
+#include "rsrr.h"
+#include "rsrr_var.h"
+#endif /* RSRR */
+
+/*
+ * Miscellaneous constants and macros.
+ */
+#define FALSE 0
+#define TRUE 1
+
+#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
+
+#define TIMER_INTERVAL ROUTE_MAX_REPORT_DELAY
+
+#define PROTOCOL_VERSION 3 /* increment when packet format/content changes */
+#define MROUTED_VERSION 9 /* not in DVMRP packets at all */
+
+#define DVMRP_CONSTANT 0x000eff00 /* constant portion of 'group' field */
+
+#define MROUTED_LEVEL (DVMRP_CONSTANT | PROTOCOL_VERSION)
+ /* for IGMP 'group' field of DVMRP messages */
+
+#define LEAF_FLAGS (( vifs_with_neighbors == 1 ) ? 0x010000 : 0)
+ /* more for IGMP 'group' field of DVMRP messages */
+
+#define DEL_RTE_GROUP 0
+#define DEL_ALL_ROUTES 1
+ /* for Deleting kernel table entries */
+
+/* obnoxious gcc gives an extraneous warning about this constant... */
+#if defined(__STDC__) || defined(__GNUC__)
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#else
+#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */
+#define const /**/
+#endif
+
+#ifdef RSRR
+#define BIT_ZERO(X) ((X) = 0)
+#define BIT_SET(X,n) ((X) |= 1 << (n))
+#define BIT_CLR(X,n) ((X) &= ~(1 << (n)))
+#define BIT_TST(X,n) ((X) & 1 << (n))
+#endif /* RSRR */
+
+#ifdef SYSV
+#define bcopy(a, b, c) memcpy(b, a, c)
+#define bzero(s, n) memset((s), 0, (n))
+#define setlinebuf(s) setvbuf(s, NULL, _IOLBF, 0)
+#endif
+
+#if defined(_AIX) || (defined(BSD) && (BSD >= 199103))
+#define HAVE_SA_LEN
+#endif
+
+/*
+ * External declarations for global variables and functions.
+ */
+#define RECV_BUF_SIZE 8192
+extern char *recv_buf;
+extern char *send_buf;
+extern int igmp_socket;
+#ifdef RSRR
+extern int rsrr_socket;
+#endif /* RSRR */
+extern u_int32 allhosts_group;
+extern u_int32 allrtrs_group;
+extern u_int32 dvmrp_group;
+extern u_int32 dvmrp_genid;
+
+#define IF_DEBUG(l) if (debug && debug & (l))
+
+#define DEBUG_PKT 0x0001
+#define DEBUG_PRUNE 0x0002
+#define DEBUG_ROUTE 0x0004
+#define DEBUG_PEER 0x0008
+#define DEBUG_CACHE 0x0010
+#define DEBUG_TIMEOUT 0x0020
+#define DEBUG_IF 0x0040
+#define DEBUG_MEMBER 0x0080
+#define DEBUG_TRACE 0x0100
+#define DEBUG_IGMP 0x0200
+#define DEBUG_RTDETAIL 0x0400
+#define DEBUG_KERN 0x0800
+#define DEBUG_RSRR 0x1000
+#define DEBUG_ICMP 0x2000
+
+#define DEFAULT_DEBUG 0x02de /* default if "-d" given without value */
+
+extern int debug;
+extern int did_final_init;
+
+extern int routes_changed;
+extern int delay_change_reports;
+extern unsigned nroutes;
+
+extern struct uvif uvifs[MAXVIFS];
+extern vifi_t numvifs;
+extern int vifs_down;
+extern int udp_socket;
+extern int vifs_with_neighbors;
+
+extern char s1[];
+extern char s2[];
+extern char s3[];
+extern char s4[];
+
+#if !(defined(BSD) && (BSD >= 199103))
+extern int errno;
+extern int sys_nerr;
+extern char * sys_errlist[];
+#endif
+
+#ifdef OLD_KERNEL
+#define MRT_INIT DVMRP_INIT
+#define MRT_DONE DVMRP_DONE
+#define MRT_ADD_VIF DVMRP_ADD_VIF
+#define MRT_DEL_VIF DVMRP_DEL_VIF
+#define MRT_ADD_MFC DVMRP_ADD_MFC
+#define MRT_DEL_MFC DVMRP_DEL_MFC
+#endif
+
+#ifndef IGMP_PIM
+#define IGMP_PIM 0x14
+#endif
+#ifndef IPPROTO_IPIP
+#define IPPROTO_IPIP 4
+#endif
+
+/*
+ * The original multicast releases defined
+ * IGMP_HOST_{MEMBERSHIP_QUERY,MEMBERSHIP_REPORT,NEW_MEMBERSHIP_REPORT
+ * ,LEAVE_MESSAGE}. Later releases removed the HOST and inserted
+ * the IGMP version number. NetBSD inserted the version number in
+ * a different way. mrouted uses the new names, so we #define them
+ * to the old ones if needed.
+ */
+#if !defined(IGMP_MEMBERSHIP_QUERY) && defined(IGMP_HOST_MEMBERSHIP_QUERY)
+#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY
+#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE
+#endif
+#ifndef IGMP_V1_MEMBERSHIP_REPORT
+#ifdef IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_HOST_NEW_MEMBERSHIP_REPORT
+#endif
+#ifdef IGMP_v1_HOST_MEMBERSHIP_REPORT
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_v1_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_v2_HOST_MEMBERSHIP_REPORT
+#endif
+#endif
+
+/*
+ * NetBSD also renamed the mtrace types.
+ */
+#if !defined(IGMP_MTRACE_RESP) && defined(IGMP_MTRACE_REPLY)
+#define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY
+#define IGMP_MTRACE IGMP_MTRACE_QUERY
+#endif
+
+/* main.c */
+extern char * scaletime __P((u_long));
+extern void log __P((int, int, char *, ...)) __printflike(3, 4);
+extern int register_input_handler __P((int, ihfunc_t));
+
+/* igmp.c */
+extern void init_igmp __P((void));
+extern void accept_igmp __P((int));
+extern void build_igmp __P((u_int32, u_int32, int, int, u_int32,
+ int));
+extern void send_igmp __P((u_int32, u_int32, int, int, u_int32,
+ int));
+extern char * igmp_packet_kind __P((u_int, u_int));
+extern int igmp_debug_kind __P((u_int, u_int));
+
+/* icmp.c */
+extern void init_icmp __P((void));
+
+/* ipip.c */
+extern void init_ipip __P((void));
+extern void init_ipip_on_vif __P((struct uvif *));
+extern void send_ipip __P((u_int32, u_int32, int, int, u_int32,
+ int, struct uvif *));
+
+/* callout.c */
+extern void callout_init __P((void));
+extern void free_all_callouts __P((void));
+extern void age_callout_queue __P((int));
+extern int timer_nextTimer __P((void));
+extern int timer_setTimer __P((int, cfunc_t, void *));
+extern int timer_clearTimer __P((int));
+extern int timer_leftTimer __P((int));
+
+/* route.c */
+extern void init_routes __P((void));
+extern void start_route_updates __P((void));
+extern void update_route __P((u_int32, u_int32, u_int, u_int32,
+ vifi_t, struct listaddr *));
+extern void age_routes __P((void));
+extern void expire_all_routes __P((void));
+extern void free_all_routes __P((void));
+extern void accept_probe __P((u_int32, u_int32, char *, int,
+ u_int32));
+extern void accept_report __P((u_int32, u_int32, char *, int,
+ u_int32));
+extern struct rtentry * determine_route __P((u_int32 src));
+extern void report __P((int, vifi_t, u_int32));
+extern void report_to_all_neighbors __P((int));
+extern int report_next_chunk __P((void));
+extern void blaster_alloc __P((vifi_t));
+extern void add_vif_to_routes __P((vifi_t));
+extern void delete_vif_from_routes __P((vifi_t));
+extern void add_neighbor_to_routes __P((vifi_t, int));
+extern void delete_neighbor_from_routes __P((u_int32,
+ vifi_t, int));
+extern void dump_routes __P((FILE *fp));
+
+/* vif.c */
+extern void init_vifs __P((void));
+extern void zero_vif __P((struct uvif *, int));
+extern void init_installvifs __P((void));
+extern void check_vif_state __P((void));
+extern void send_on_vif __P((struct uvif *, u_int32, int, int));
+extern vifi_t find_vif __P((u_int32, u_int32));
+extern void age_vifs __P((void));
+extern void dump_vifs __P((FILE *));
+extern void stop_all_vifs __P((void));
+extern struct listaddr *neighbor_info __P((vifi_t, u_int32));
+extern void accept_group_report __P((u_int32, u_int32,
+ u_int32, int));
+extern void query_groups __P((void));
+extern void probe_for_neighbors __P((void));
+extern struct listaddr *update_neighbor __P((vifi_t, u_int32, int, char *, int,
+ u_int32));
+extern void accept_neighbor_request __P((u_int32, u_int32));
+extern void accept_neighbor_request2 __P((u_int32, u_int32));
+extern void accept_info_request __P((u_int32, u_int32,
+ u_char *, int));
+extern void accept_info_reply __P((u_int32, u_int32,
+ u_char *, int));
+extern void accept_neighbors __P((u_int32, u_int32,
+ u_char *, int, u_int32));
+extern void accept_neighbors2 __P((u_int32, u_int32,
+ u_char *, int, u_int32));
+extern void accept_leave_message __P((u_int32, u_int32,
+ u_int32));
+extern void accept_membership_query __P((u_int32, u_int32,
+ u_int32, int));
+
+/* config.c */
+extern void config_vifs_from_kernel __P((void));
+
+/* cfparse.y */
+extern void config_vifs_from_file __P((void));
+
+/* inet.c */
+extern int inet_valid_host __P((u_int32));
+extern int inet_valid_mask __P((u_int32));
+extern int inet_valid_subnet __P((u_int32, u_int32));
+extern char * inet_fmt __P((u_int32, char *));
+extern char * inet_fmts __P((u_int32, u_int32, char *));
+extern u_int32 inet_parse __P((char *, int));
+extern int inet_cksum __P((u_short *, u_int));
+
+/* prune.c */
+extern unsigned kroutes;
+extern void determine_forwvifs __P((struct gtable *));
+extern void send_prune_or_graft __P((struct gtable *));
+extern void add_table_entry __P((u_int32, u_int32));
+extern void del_table_entry __P((struct rtentry *,
+ u_int32, u_int));
+extern void update_table_entry __P((struct rtentry *, u_int32));
+extern int find_src_grp __P((u_int32, u_int32, u_int32));
+extern void init_ktable __P((void));
+extern void steal_sources __P((struct rtentry *));
+extern void reset_neighbor_state __P((vifi_t, u_int32));
+extern int grplst_mem __P((vifi_t, u_int32));
+extern void free_all_prunes __P((void));
+extern void age_table_entry __P((void));
+extern void dump_cache __P((FILE *));
+extern void update_lclgrp __P((vifi_t, u_int32));
+extern void delete_lclgrp __P((vifi_t, u_int32));
+extern void chkgrp_graft __P((vifi_t, u_int32));
+extern void accept_prune __P((u_int32, u_int32, char *, int));
+extern void accept_graft __P((u_int32, u_int32, char *, int));
+extern void accept_g_ack __P((u_int32, u_int32, char *, int));
+/* u_int is promoted u_char */
+extern void accept_mtrace __P((u_int32, u_int32,
+ u_int32, char *, u_int, int));
+
+/* kern.c */
+extern void k_set_rcvbuf __P((int, int));
+extern void k_hdr_include __P((int));
+extern void k_set_ttl __P((int));
+extern void k_set_loop __P((int));
+extern void k_set_if __P((u_int32));
+extern void k_join __P((u_int32, u_int32));
+extern void k_leave __P((u_int32, u_int32));
+extern void k_init_dvmrp __P((void));
+extern void k_stop_dvmrp __P((void));
+extern void k_add_vif __P((vifi_t, struct uvif *));
+extern void k_del_vif __P((vifi_t));
+extern void k_add_rg __P((u_int32, struct gtable *));
+extern int k_del_rg __P((u_int32, struct gtable *));
+extern int k_get_version __P((void));
+
+#ifdef SNMP
+/* prune.c */
+extern struct gtable * find_grp __P((u_int32));
+extern struct stable * find_grp_src __P((struct gtable *, u_int32));
+extern int next_grp_src_mask __P((struct gtable **,
+ struct stable **, u_int32,
+ u_int32, u_int32));
+extern void refresh_sg __P((struct sioc_sg_req *, struct gtable *,
+ struct stable *));
+extern int next_child __P((struct gtable **, struct stable **,
+ u_int32, u_int32, u_int32,
+ vifi_t *));
+
+/* route.c */
+extern struct rtentry * snmp_find_route __P((u_int32, u_int32));
+extern int next_route __P((struct rtentry **, u_int32, u_int32));
+extern int next_route_child __P((struct rtentry **,
+ u_int32, u_int32, vifi_t *));
+#endif
+
+#ifdef RSRR
+/* prune.c */
+extern struct gtable *kernel_table;
+extern struct gtable *gtp;
+
+/* rsrr.c */
+extern void rsrr_init __P((void));
+extern void rsrr_clean __P((void));
+extern void rsrr_cache_send __P((struct gtable *, int));
+extern void rsrr_cache_clean __P((struct gtable *));
+#endif /* RSRR */
diff --git a/usr.sbin/mrouted/dvmrp.h b/usr.sbin/mrouted/dvmrp.h
new file mode 100644
index 0000000..2f11872
--- /dev/null
+++ b/usr.sbin/mrouted/dvmrp.h
@@ -0,0 +1,172 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * dvmrp.h,v 3.8.4.5 1997/11/18 23:25:57 fenner Exp
+ */
+
+/*
+ * A DVMRP message consists of an IP header + an IGMP header + (for some types)
+ * zero or more bytes of data.
+ *
+ * For REPORT messages, the data is route information; the route information
+ * consists of one or more lists of the following form:
+ *
+ * (mask, (origin, metric), (origin, metric), ...)
+ *
+ * where:
+ *
+ * "mask" is the subnet mask for all the origins in the list.
+ * It is always THREE bytes long, containing the low-order
+ * three bytes of the mask (the high-order byte is always
+ * 0xff and therefore need not be transmitted).
+ *
+ * "origin" is the number of a subnet from which multicast datagrams
+ * may originate. It is from one to four bytes long,
+ * depending on the value of "mask":
+ * if all bytes of the mask are zero
+ * the subnet number is one byte long
+ * else if the low-order two bytes of the mask are zero
+ * the subnet number is two bytes long
+ * else if the lowest-order byte of the mask is zero
+ * the subnet number is three bytes long,
+ * else
+ * the subnet number is four bytes long.
+ *
+ * "metric" is a one-byte value consisting of two subfields:
+ * - the high-order bit is a flag which, when set, indicates
+ * the last (origin, metric) pair of a list.
+ * - the low-order seven bits contain the routing metric for
+ * the corresponding origin, relative to the sender of the
+ * DVMRP report. The metric may have the value of UNREACHABLE
+ * added to it as a "split horizon" indication (so called
+ * "poisoned reverse").
+ *
+ * Within a list, the origin subnet numbers must be in ascending order, and
+ * the lists themselves are in order of increasing mask value. A message may
+ * not exceed 576 bytes, the default maximum IP reassembly size, including
+ * the IP and IGMP headers; the route information may be split across more
+ * than one message if necessary, by terminating a list in one message and
+ * starting a new list in the next message (repeating the same mask value,
+ * if necessary).
+ *
+ * For NEIGHBORS messages, the data is neighboring-router information
+ * consisting of one or more lists of the following form:
+ *
+ * (local-addr, metric, threshold, ncount, neighbor, neighbor, ...)
+ *
+ * where:
+ *
+ * "local-addr" is the sending router's address as seen by the neighbors
+ * in this list; it is always four bytes long.
+ * "metric" is a one-byte unsigned value, the TTL `cost' of forwarding
+ * packets to any of the neighbors on this list.
+ * "threshold" is a one-byte unsigned value, a lower bound on the TTL a
+ * packet must have to be forwarded to any of the neighbors on
+ * this list.
+ * "ncount" is the number of neighbors in this list.
+ * "neighbor" is the address of a neighboring router, four bytes long.
+ *
+ * As with REPORT messages, NEIGHBORS messages should not exceed 576 bytes,
+ * including the IP and IGMP headers; split longer messages by terminating the
+ * list in one and continuing in another, repeating the local-addr, etc., if
+ * necessary.
+ *
+ * For NEIGHBORS2 messages, the data is identical to NEIGHBORS except
+ * there is a flags byte before the neighbor count:
+ *
+ * (local-addr, metric, threshold, flags, ncount, neighbor, neighbor, ...)
+ */
+
+/*
+ * DVMRP message types (carried in the "code" field of an IGMP header)
+ */
+#define DVMRP_PROBE 1 /* for finding neighbors */
+#define DVMRP_REPORT 2 /* for reporting some or all routes */
+#define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */
+ /* of this router's neighbors. */
+#define DVMRP_NEIGHBORS 4 /* response to such a request */
+#define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */
+#define DVMRP_NEIGHBORS2 6
+#define DVMRP_PRUNE 7 /* prune message */
+#define DVMRP_GRAFT 8 /* graft message */
+#define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */
+#define DVMRP_INFO_REQUEST 10 /* information request */
+#define DVMRP_INFO_REPLY 11 /* information reply */
+
+/*
+ * 'flags' byte values in DVMRP_NEIGHBORS2 reply.
+ */
+#define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */
+#define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */
+#define DVMRP_NF_PIM 0x04 /* neighbor is a PIM neighbor */
+#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
+#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
+#define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */
+#define DVMRP_NF_LEAF 0x80 /* Neighbor reports that it is a leaf */
+
+/*
+ * Request/reply types for info queries/replies
+ */
+#define DVMRP_INFO_VERSION 1 /* version string */
+#define DVMRP_INFO_NEIGHBORS 2 /* neighbors2 data */
+
+/*
+ * Limit on length of route data
+ */
+#define MAX_IP_PACKET_LEN 576
+#define MIN_IP_HEADER_LEN 20
+#define MAX_IP_HEADER_LEN 60
+#define MAX_DVMRP_DATA_LEN \
+ ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN )
+
+/*
+ * Various protocol constants (all times in seconds)
+ */
+ /* address for multicast DVMRP msgs */
+#define INADDR_DVMRP_GROUP (u_int32)0xe0000004 /* 224.0.0.4 */
+/*
+ * The IGMPv2 <netinet/in.h> defines INADDR_ALLRTRS_GROUP, but earlier
+ * ones don't, so we define it conditionally here.
+ */
+#ifndef INADDR_ALLRTRS_GROUP
+ /* address for multicast mtrace msg */
+#define INADDR_ALLRTRS_GROUP (u_int32)0xe0000002 /* 224.0.0.2 */
+#endif
+
+#define ROUTE_MAX_REPORT_DELAY 5 /* max delay for reporting changes */
+ /* (This is the timer interrupt */
+ /* interval; all times must be */
+ /* multiples of this value.) */
+
+#define ROUTE_REPORT_INTERVAL 60 /* periodic route report interval */
+#define ROUTE_SWITCH_TIME 140 /* time to switch to equivalent gw */
+#define ROUTE_EXPIRE_TIME 200 /* time to mark route invalid */
+#define ROUTE_DISCARD_TIME 340 /* time to garbage collect route */
+
+#define LEAF_CONFIRMATION_TIME 200 /* time to consider subnet a leaf */
+
+#define NEIGHBOR_PROBE_INTERVAL 10 /* periodic neighbor probe interval */
+#define NEIGHBOR_EXPIRE_TIME 30 /* time to consider neighbor gone */
+#define OLD_NEIGHBOR_EXPIRE_TIME 140 /* time to consider neighbor gone */
+
+#define UNREACHABLE 32 /* "infinity" metric, must be <= 64 */
+#define DEFAULT_METRIC 1 /* default subnet/tunnel metric */
+#define DEFAULT_THRESHOLD 1 /* default subnet/tunnel threshold */
+
+#define MAX_RATE_LIMIT 100000 /* max rate limit */
+#define DEFAULT_PHY_RATE_LIMIT 0 /* default phyint rate limit */
+#define DEFAULT_TUN_RATE_LIMIT 0 /* default tunnel rate limit */
+
+#define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */
+#define MIN_CACHE_LIFETIME 60 /* minimum allowed cache lifetime */
+#define AVERAGE_PRUNE_LIFETIME 7200 /* average lifetime of prunes sent */
+#define MIN_PRUNE_LIFETIME 120 /* minimum allowed prune lifetime */
+#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */
+#define PRUNE_REXMIT_VAL 3 /* initial time for prune rexmission*/
diff --git a/usr.sbin/mrouted/icmp.c b/usr.sbin/mrouted/icmp.c
new file mode 100644
index 0000000..72efa0a
--- /dev/null
+++ b/usr.sbin/mrouted/icmp.c
@@ -0,0 +1,225 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp
+ */
+
+#include "defs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp $";
+#endif
+
+static int icmp_socket;
+
+static void icmp_handler __P((int, fd_set *));
+static char * icmp_name __P((struct icmp *));
+
+void
+init_icmp()
+{
+ if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
+ log(LOG_ERR, errno, "ICMP socket");
+
+ register_input_handler(icmp_socket, icmp_handler);
+
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "registering icmp socket fd %d\n", icmp_socket);
+}
+
+static void
+icmp_handler(fd, rfds)
+ int fd;
+ fd_set *rfds;
+{
+ u_char icmp_buf[RECV_BUF_SIZE];
+ struct sockaddr_in from;
+ int fromlen, recvlen, iphdrlen, ipdatalen;
+ struct icmp *icmp;
+ struct ip *ip;
+ vifi_t i;
+ struct uvif *v;
+ u_int32 src;
+
+ fromlen = sizeof(from);
+ recvlen = recvfrom(icmp_socket, icmp_buf, RECV_BUF_SIZE, 0,
+ (struct sockaddr *)&from, &fromlen);
+ if (recvlen < 0) {
+ if (errno != EINTR)
+ log(LOG_WARNING, errno, "icmp_socket recvfrom");
+ return;
+ }
+ ip = (struct ip *)icmp_buf;
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "hdr %d data %d != rcv %d", iphdrlen, ipdatalen, recvlen);
+ /* Malformed ICMP, just return. */
+ return;
+ }
+ if (ipdatalen < ICMP_MINLEN + sizeof(struct ip)) {
+ /* Not enough data for us to be interested in it. */
+ return;
+ }
+ src = ip->ip_src.s_addr;
+ icmp = (struct icmp *)(icmp_buf + iphdrlen);
+ IF_DEBUG(DEBUG_ICMP)
+ log(LOG_DEBUG, 0, "got ICMP type %d from %s",
+ icmp->icmp_type, inet_fmt(src, s1));
+ /*
+ * Eventually:
+ * have registry of ICMP listeners, by type, code and ICMP_ID
+ * (and maybe fields of the original packet too -- maybe need a
+ * generalized packet filter!) to allow ping and traceroute
+ * from the monitoring tool.
+ */
+ switch (icmp->icmp_type) {
+ case ICMP_UNREACH:
+ case ICMP_TIMXCEED:
+ /* Look at returned packet to see if it's us sending on a tunnel */
+ ip = &icmp->icmp_ip;
+ if (ip->ip_p != IPPROTO_IGMP && ip->ip_p != IPPROTO_IPIP)
+ return;
+ for (v = uvifs, i = 0; i < numvifs; v++, i++) {
+ if (ip->ip_src.s_addr == v->uv_lcl_addr &&
+ ip->ip_dst.s_addr == v->uv_dst_addr) {
+ char *p;
+ int n;
+ /*
+ * I sent this packet on this vif.
+ */
+ n = ++v->uv_icmp_warn;
+ while (n && !(n & 1))
+ n >>= 1;
+ if (n == 1 && ((p = icmp_name(icmp)) != NULL))
+ log(LOG_WARNING, 0, "Received ICMP %s from %s %s %s on vif %d",
+ p, inet_fmt(src, s1), "for traffic sent to",
+ inet_fmt(ip->ip_dst.s_addr, s2),
+ i);
+
+ break;
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * Return NULL for ICMP informational messages.
+ * Return string describing the error for ICMP errors.
+ */
+static char *
+icmp_name(icmp)
+ struct icmp *icmp;
+{
+ static char retval[30];
+
+ switch (icmp->icmp_type) {
+ case ICMP_UNREACH:
+ switch (icmp->icmp_code) {
+ case ICMP_UNREACH_NET:
+ return "network unreachable";
+ case ICMP_UNREACH_HOST:
+ return "host unreachable";
+ case ICMP_UNREACH_PROTOCOL:
+ return "protocol unreachable";
+ case ICMP_UNREACH_PORT:
+ return "port unreachable";
+ case ICMP_UNREACH_NEEDFRAG:
+ return "needs fragmentation";
+ case ICMP_UNREACH_SRCFAIL:
+ return "source route failed";
+#ifndef ICMP_UNREACH_NET_UNKNOWN
+#define ICMP_UNREACH_NET_UNKNOWN 6
+#endif
+ case ICMP_UNREACH_NET_UNKNOWN:
+ return "network unknown";
+#ifndef ICMP_UNREACH_HOST_UNKNOWN
+#define ICMP_UNREACH_HOST_UNKNOWN 7
+#endif
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ return "host unknown";
+#ifndef ICMP_UNREACH_ISOLATED
+#define ICMP_UNREACH_ISOLATED 8
+#endif
+ case ICMP_UNREACH_ISOLATED:
+ return "source host isolated";
+#ifndef ICMP_UNREACH_NET_PROHIB
+#define ICMP_UNREACH_NET_PROHIB 9
+#endif
+ case ICMP_UNREACH_NET_PROHIB:
+ return "network access prohibited";
+#ifndef ICMP_UNREACH_HOST_PROHIB
+#define ICMP_UNREACH_HOST_PROHIB 10
+#endif
+ case ICMP_UNREACH_HOST_PROHIB:
+ return "host access prohibited";
+#ifndef ICMP_UNREACH_TOSNET
+#define ICMP_UNREACH_TOSNET 11
+#endif
+ case ICMP_UNREACH_TOSNET:
+ return "bad TOS for net";
+#ifndef ICMP_UNREACH_TOSHOST
+#define ICMP_UNREACH_TOSHOST 12
+#endif
+ case ICMP_UNREACH_TOSHOST:
+ return "bad TOS for host";
+#ifndef ICMP_UNREACH_FILTER_PROHIB
+#define ICMP_UNREACH_FILTER_PROHIB 13
+#endif
+ case ICMP_UNREACH_FILTER_PROHIB:
+ return "prohibited by filter";
+#ifndef ICMP_UNREACH_HOST_PRECEDENCE
+#define ICMP_UNREACH_HOST_PRECEDENCE 14
+#endif
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ return "host precedence violation";
+#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
+#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
+#endif
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ return "precedence cutoff";
+ default:
+ sprintf(retval, "unreachable code %d", icmp->icmp_code);
+ return retval;
+ }
+ case ICMP_SOURCEQUENCH:
+ return "source quench";
+ case ICMP_REDIRECT:
+ return NULL; /* XXX */
+ case ICMP_TIMXCEED:
+ switch (icmp->icmp_code) {
+ case ICMP_TIMXCEED_INTRANS:
+ return "time exceeded in transit";
+ case ICMP_TIMXCEED_REASS:
+ return "time exceeded in reassembly";
+ default:
+ sprintf(retval, "time exceeded code %d", icmp->icmp_code);
+ return retval;
+ }
+ case ICMP_PARAMPROB:
+ switch (icmp->icmp_code) {
+#ifndef ICMP_PARAMPROB_OPTABSENT
+#define ICMP_PARAMPROB_OPTABSENT 1
+#endif
+ case ICMP_PARAMPROB_OPTABSENT:
+ return "required option absent";
+ default:
+ sprintf(retval, "parameter problem code %d", icmp->icmp_code);
+ return retval;
+ }
+ }
+ return NULL;
+}
diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c
new file mode 100644
index 0000000..f343035
--- /dev/null
+++ b/usr.sbin/mrouted/igmp.c
@@ -0,0 +1,447 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * igmp.c,v 3.8.4.19 1998/01/06 01:57:43 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Exported variables.
+ */
+char *recv_buf; /* input packet buffer */
+char *send_buf; /* output packet buffer */
+int igmp_socket; /* socket for all network I/O */
+u_int32 allhosts_group; /* All hosts addr in net order */
+u_int32 allrtrs_group; /* All-Routers " in net order */
+u_int32 dvmrp_group; /* DVMRP grp addr in net order */
+u_int32 dvmrp_genid; /* IGMP generation id */
+
+/*
+ * Local function definitions.
+ */
+/* u_char promoted to u_int */
+static int igmp_log_level __P((u_int type, u_int code));
+
+/*
+ * Open and initialize the igmp socket, and fill in the non-changing
+ * IP header fields in the output packet buffer.
+ */
+void
+init_igmp()
+{
+ struct ip *ip;
+
+ recv_buf = malloc(RECV_BUF_SIZE);
+ send_buf = malloc(RECV_BUF_SIZE);
+
+ if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
+ log(LOG_ERR, errno, "IGMP socket");
+
+ k_hdr_include(TRUE); /* include IP header when sending */
+ k_set_rcvbuf(256*1024,48*1024); /* lots of input buffering */
+ k_set_ttl(1); /* restrict multicasts to one hop */
+ k_set_loop(FALSE); /* disable multicast loopback */
+
+ ip = (struct ip *)send_buf;
+ bzero(ip, sizeof(struct ip));
+ /*
+ * Fields zeroed that aren't filled in later:
+ * - IP ID (let the kernel fill it in)
+ * - Offset (we don't send fragments)
+ * - Checksum (let the kernel fill it in)
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = 0xc0; /* Internet Control */
+ ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+ ip->ip_p = IPPROTO_IGMP;
+
+ allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+ dvmrp_group = htonl(INADDR_DVMRP_GROUP);
+ allrtrs_group = htonl(INADDR_ALLRTRS_GROUP);
+}
+
+#define PIM_QUERY 0
+#define PIM_REGISTER 1
+#define PIM_REGISTER_STOP 2
+#define PIM_JOIN_PRUNE 3
+#define PIM_RP_REACHABLE 4
+#define PIM_ASSERT 5
+#define PIM_GRAFT 6
+#define PIM_GRAFT_ACK 7
+
+char *
+igmp_packet_kind(type, code)
+ u_int type, code;
+{
+ static char unknown[20];
+
+ switch (type) {
+ case IGMP_MEMBERSHIP_QUERY: return "membership query ";
+ case IGMP_V1_MEMBERSHIP_REPORT: return "V1 member report ";
+ case IGMP_V2_MEMBERSHIP_REPORT: return "V2 member report ";
+ case IGMP_V2_LEAVE_GROUP: return "leave message ";
+ case IGMP_DVMRP:
+ switch (code) {
+ case DVMRP_PROBE: return "neighbor probe ";
+ case DVMRP_REPORT: return "route report ";
+ case DVMRP_ASK_NEIGHBORS: return "neighbor request ";
+ case DVMRP_NEIGHBORS: return "neighbor list ";
+ case DVMRP_ASK_NEIGHBORS2: return "neighbor request 2";
+ case DVMRP_NEIGHBORS2: return "neighbor list 2 ";
+ case DVMRP_PRUNE: return "prune message ";
+ case DVMRP_GRAFT: return "graft message ";
+ case DVMRP_GRAFT_ACK: return "graft message ack ";
+ case DVMRP_INFO_REQUEST: return "info request ";
+ case DVMRP_INFO_REPLY: return "info reply ";
+ default:
+ sprintf(unknown, "unknown DVMRP %3d ", code);
+ return unknown;
+ }
+ case IGMP_PIM:
+ switch (code) {
+ case PIM_QUERY: return "PIM Router-Query ";
+ case PIM_REGISTER: return "PIM Register ";
+ case PIM_REGISTER_STOP: return "PIM Register-Stop ";
+ case PIM_JOIN_PRUNE: return "PIM Join/Prune ";
+ case PIM_RP_REACHABLE: return "PIM RP-Reachable ";
+ case PIM_ASSERT: return "PIM Assert ";
+ case PIM_GRAFT: return "PIM Graft ";
+ case PIM_GRAFT_ACK: return "PIM Graft-Ack ";
+ default:
+ sprintf(unknown, "unknown PIM msg%3d", code);
+ return unknown;
+ }
+ case IGMP_MTRACE: return "IGMP trace query ";
+ case IGMP_MTRACE_RESP: return "IGMP trace reply ";
+ default:
+ sprintf(unknown, "unk: 0x%02x/0x%02x ", type, code);
+ return unknown;
+ }
+}
+
+int
+igmp_debug_kind(type, code)
+ u_int type, code;
+{
+ switch (type) {
+ case IGMP_MEMBERSHIP_QUERY: return DEBUG_IGMP;
+ case IGMP_V1_MEMBERSHIP_REPORT: return DEBUG_IGMP;
+ case IGMP_V2_MEMBERSHIP_REPORT: return DEBUG_IGMP;
+ case IGMP_V2_LEAVE_GROUP: return DEBUG_IGMP;
+ case IGMP_DVMRP:
+ switch (code) {
+ case DVMRP_PROBE: return DEBUG_PEER;
+ case DVMRP_REPORT: return DEBUG_ROUTE;
+ case DVMRP_ASK_NEIGHBORS: return 0;
+ case DVMRP_NEIGHBORS: return 0;
+ case DVMRP_ASK_NEIGHBORS2: return 0;
+ case DVMRP_NEIGHBORS2: return 0;
+ case DVMRP_PRUNE: return DEBUG_PRUNE;
+ case DVMRP_GRAFT: return DEBUG_PRUNE;
+ case DVMRP_GRAFT_ACK: return DEBUG_PRUNE;
+ case DVMRP_INFO_REQUEST: return 0;
+ case DVMRP_INFO_REPLY: return 0;
+ default: return 0;
+ }
+ case IGMP_PIM:
+ switch (code) {
+ case PIM_QUERY: return 0;
+ case PIM_REGISTER: return 0;
+ case PIM_REGISTER_STOP: return 0;
+ case PIM_JOIN_PRUNE: return 0;
+ case PIM_RP_REACHABLE: return 0;
+ case PIM_ASSERT: return 0;
+ case PIM_GRAFT: return 0;
+ case PIM_GRAFT_ACK: return 0;
+ default: return 0;
+ }
+ case IGMP_MTRACE: return DEBUG_TRACE;
+ case IGMP_MTRACE_RESP: return DEBUG_TRACE;
+ default: return DEBUG_IGMP;
+ }
+}
+
+/*
+ * Process a newly received IGMP packet that is sitting in the input
+ * packet buffer.
+ */
+void
+accept_igmp(recvlen)
+ int recvlen;
+{
+ register u_int32 src, dst, group;
+ struct ip *ip;
+ struct igmp *igmp;
+ int ipdatalen, iphdrlen, igmpdatalen;
+
+ if (recvlen < sizeof(struct ip)) {
+ log(LOG_WARNING, 0,
+ "received packet too short (%u bytes) for IP header", recvlen);
+ return;
+ }
+
+ ip = (struct ip *)recv_buf;
+ src = ip->ip_src.s_addr;
+ dst = ip->ip_dst.s_addr;
+
+ /*
+ * this is most likely a message from the kernel indicating that
+ * a new src grp pair message has arrived and so, it would be
+ * necessary to install a route into the kernel for this.
+ */
+ if (ip->ip_p == 0) {
+ if (src == 0 || dst == 0)
+ log(LOG_WARNING, 0, "kernel request not accurate");
+ else
+ add_table_entry(src, dst);
+ return;
+ }
+
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ log(LOG_WARNING, 0,
+ "received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
+ inet_fmt(src, s1), recvlen, iphdrlen, ipdatalen);
+ return;
+ }
+
+ igmp = (struct igmp *)(recv_buf + iphdrlen);
+ group = igmp->igmp_group.s_addr;
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ log(LOG_WARNING, 0,
+ "received IP data field too short (%u bytes) for IGMP, from %s",
+ ipdatalen, inet_fmt(src, s1));
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(igmp->igmp_type, igmp->igmp_code))
+ log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
+ igmp_packet_kind(igmp->igmp_type, igmp->igmp_code),
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_MEMBERSHIP_QUERY:
+ accept_membership_query(src, dst, group, igmp->igmp_code);
+ return;
+
+ case IGMP_V1_MEMBERSHIP_REPORT:
+ case IGMP_V2_MEMBERSHIP_REPORT:
+ accept_group_report(src, dst, group, igmp->igmp_type);
+ return;
+
+ case IGMP_V2_LEAVE_GROUP:
+ accept_leave_message(src, dst, group);
+ return;
+
+ case IGMP_DVMRP:
+ group = ntohl(group);
+
+ switch (igmp->igmp_code) {
+ case DVMRP_PROBE:
+ accept_probe(src, dst,
+ (char *)(igmp+1), igmpdatalen, group);
+ return;
+
+ case DVMRP_REPORT:
+ accept_report(src, dst,
+ (char *)(igmp+1), igmpdatalen, group);
+ return;
+
+ case DVMRP_ASK_NEIGHBORS:
+ accept_neighbor_request(src, dst);
+ return;
+
+ case DVMRP_ASK_NEIGHBORS2:
+ accept_neighbor_request2(src, dst);
+ return;
+
+ case DVMRP_NEIGHBORS:
+ accept_neighbors(src, dst, (u_char *)(igmp+1), igmpdatalen,
+ group);
+ return;
+
+ case DVMRP_NEIGHBORS2:
+ accept_neighbors2(src, dst, (u_char *)(igmp+1), igmpdatalen,
+ group);
+ return;
+
+ case DVMRP_PRUNE:
+ accept_prune(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ case DVMRP_GRAFT:
+ accept_graft(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ case DVMRP_GRAFT_ACK:
+ accept_g_ack(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ case DVMRP_INFO_REQUEST:
+ accept_info_request(src, dst, (char *)(igmp+1),
+ igmpdatalen);
+ return;
+
+ case DVMRP_INFO_REPLY:
+ accept_info_reply(src, dst, (char *)(igmp+1), igmpdatalen);
+ return;
+
+ default:
+ log(LOG_INFO, 0,
+ "ignoring unknown DVMRP message code %u from %s to %s",
+ igmp->igmp_code, inet_fmt(src, s1),
+ inet_fmt(dst, s2));
+ return;
+ }
+
+ case IGMP_PIM:
+ return;
+
+ case IGMP_MTRACE_RESP:
+ return;
+
+ case IGMP_MTRACE:
+ accept_mtrace(src, dst, group, (char *)(igmp+1),
+ igmp->igmp_code, igmpdatalen);
+ return;
+
+ default:
+ log(LOG_INFO, 0,
+ "ignoring unknown IGMP message type %x from %s to %s",
+ igmp->igmp_type, inet_fmt(src, s1),
+ inet_fmt(dst, s2));
+ return;
+ }
+}
+
+/*
+ * Some IGMP messages are more important than others. This routine
+ * determines the logging level at which to log a send error (often
+ * "No route to host"). This is important when there is asymmetric
+ * reachability and someone is trying to, i.e., mrinfo me periodically.
+ */
+static int
+igmp_log_level(type, code)
+ u_int type, code;
+{
+ switch (type) {
+ case IGMP_MTRACE_RESP:
+ return LOG_INFO;
+
+ case IGMP_DVMRP:
+ switch (code) {
+ case DVMRP_NEIGHBORS:
+ case DVMRP_NEIGHBORS2:
+ return LOG_INFO;
+ }
+ }
+ return LOG_WARNING;
+}
+
+/*
+ * Construct an IGMP message in the output packet buffer. The caller may
+ * have already placed data in that buffer, of length 'datalen'.
+ */
+void
+build_igmp(src, dst, type, code, group, datalen)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+{
+ struct ip *ip;
+ struct igmp *igmp;
+ extern int curttl;
+
+ ip = (struct ip *)send_buf;
+ ip->ip_src.s_addr = src;
+ ip->ip_dst.s_addr = dst;
+ ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+ if (IN_MULTICAST(ntohl(dst))) {
+ ip->ip_ttl = curttl;
+ } else {
+ ip->ip_ttl = MAXTTL;
+ }
+
+ igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
+ igmp->igmp_type = type;
+ igmp->igmp_code = code;
+ igmp->igmp_group.s_addr = group;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = inet_cksum((u_short *)igmp,
+ IGMP_MINLEN + datalen);
+}
+
+/*
+ * Call build_igmp() to build an IGMP message in the output packet buffer.
+ * Then send the message from the interface with IP address 'src' to
+ * destination 'dst'.
+ */
+void
+send_igmp(src, dst, type, code, group, datalen)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+{
+ struct sockaddr_in sdst;
+ int setloop = 0;
+
+ build_igmp(src, dst, type, code, group, datalen);
+
+ if (IN_MULTICAST(ntohl(dst))) {
+ k_set_if(src);
+ if (type != IGMP_DVMRP || dst == allhosts_group) {
+ setloop = 1;
+ k_set_loop(TRUE);
+ }
+ }
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ sdst.sin_len = sizeof(sdst);
+#endif
+ sdst.sin_addr.s_addr = dst;
+ if (sendto(igmp_socket, send_buf,
+ MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen, 0,
+ (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(igmp_log_level(type, code), errno,
+ "sendto to %s on %s",
+ inet_fmt(dst, s1), inet_fmt(src, s2));
+ }
+
+ if (setloop)
+ k_set_loop(FALSE);
+
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
+ igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
diff --git a/usr.sbin/mrouted/igmpv2.h b/usr.sbin/mrouted/igmpv2.h
new file mode 100644
index 0000000..5f5ae27
--- /dev/null
+++ b/usr.sbin/mrouted/igmpv2.h
@@ -0,0 +1,42 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * igmpv2.h,v 3.8.4.1 1997/11/18 23:25:58 fenner Exp
+ */
+
+/*
+ * Constants for IGMP Version 2. Several of these, especially the
+ * robustness variable, should be variables and not constants.
+ */
+#define IGMP_ROBUSTNESS_VARIABLE 2
+#define IGMP_QUERY_INTERVAL 125
+#define IGMP_QUERY_RESPONSE_INTERVAL 10
+#define IGMP_GROUP_MEMBERSHIP_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \
+ IGMP_QUERY_INTERVAL + \
+ IGMP_QUERY_RESPONSE_INTERVAL)
+#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (IGMP_ROBUSTNESS_VARIABLE * \
+ IGMP_QUERY_INTERVAL + \
+ IGMP_QUERY_RESPONSE_INTERVAL / 2)
+ /* Round to the nearest TIMER_INTERVAL */
+#define IGMP_STARTUP_QUERY_INTERVAL (((IGMP_QUERY_INTERVAL / 4) \
+ / TIMER_INTERVAL) * TIMER_INTERVAL)
+#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE
+#define IGMP_LAST_MEMBER_QUERY_INTERVAL 1
+#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS_VARIABLE
+
+/*
+ * OLD_AGE_THRESHOLD is the number of IGMP_QUERY_INTERVAL's to remember the
+ * presence of an IGMPv1 group member. According to the IGMPv2 specification,
+ * routers remember this presence for [Robustness Variable] * [Query Interval] +
+ * [Query Response Interval]. However, OLD_AGE_THRESHOLD is in units of
+ * [Query Interval], so doesn't have sufficient resolution to represent
+ * [Query Response Interval]. When the timer mechanism gets an efficient
+ * method of refreshing timers, this should get fixed.
+ */
+#define OLD_AGE_THRESHOLD IGMP_ROBUSTNESS_VARIABLE
diff --git a/usr.sbin/mrouted/inet.c b/usr.sbin/mrouted/inet.c
new file mode 100644
index 0000000..53aad7c
--- /dev/null
+++ b/usr.sbin/mrouted/inet.c
@@ -0,0 +1,235 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * inet.c,v 3.8.4.2 1998/01/06 01:57:44 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Exported variables.
+ */
+char s1[19]; /* buffers to hold the string representations */
+char s2[19]; /* of IP addresses, to be passed to inet_fmt() */
+char s3[19]; /* or inet_fmts(). */
+char s4[19];
+
+
+/*
+ * Verify that a given IP address is credible as a host address.
+ * (Without a mask, cannot detect addresses of the form {subnet,0} or
+ * {subnet,-1}.)
+ */
+int
+inet_valid_host(naddr)
+ u_int32 naddr;
+{
+ register u_int32 addr;
+
+ addr = ntohl(naddr);
+
+ return (!(IN_MULTICAST(addr) ||
+ IN_BADCLASS (addr) ||
+ (addr & 0xff000000) == 0));
+}
+
+/*
+ * Verify that a given netmask is plausible;
+ * make sure that it is a series of 1's followed by
+ * a series of 0's with no discontiguous 1's.
+ */
+int
+inet_valid_mask(mask)
+ u_int32 mask;
+{
+ if (~(((mask & -mask) - 1) | mask) != 0) {
+ /* Mask is not contiguous */
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Verify that a given subnet number and mask pair are credible.
+ *
+ * With CIDR, almost any subnet and mask are credible. mrouted still
+ * can't handle aggregated class A's, so we still check that, but
+ * otherwise the only requirements are that the subnet address is
+ * within the [ABC] range and that the host bits of the subnet
+ * are all 0.
+ */
+int
+inet_valid_subnet(nsubnet, nmask)
+ u_int32 nsubnet, nmask;
+{
+ register u_int32 subnet, mask;
+
+ subnet = ntohl(nsubnet);
+ mask = ntohl(nmask);
+
+ if ((subnet & mask) != subnet) return (FALSE);
+
+ if (subnet == 0)
+ return (mask == 0);
+
+ if (IN_CLASSA(subnet)) {
+ if (mask < 0xff000000 ||
+ (subnet & 0xff000000) == 0x7f000000 ||
+ (subnet & 0xff000000) == 0x00000000) return (FALSE);
+ }
+ else if (IN_CLASSD(subnet) || IN_BADCLASS(subnet)) {
+ /* Above Class C address space */
+ return (FALSE);
+ }
+ if (subnet & ~mask) {
+ /* Host bits are set in the subnet */
+ return (FALSE);
+ }
+ if (!inet_valid_mask(mask)) {
+ /* Netmask is not contiguous */
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Convert an IP address in u_long (network) format into a printable string.
+ */
+char *
+inet_fmt(addr, s)
+ u_int32 addr;
+ char *s;
+{
+ register u_char *a;
+
+ a = (u_char *)&addr;
+ sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
+ return (s);
+}
+
+
+/*
+ * Convert an IP subnet number in u_long (network) format into a printable
+ * string including the netmask as a number of bits.
+ */
+char *
+inet_fmts(addr, mask, s)
+ u_int32 addr, mask;
+ char *s;
+{
+ register u_char *a, *m;
+ int bits;
+
+ if ((addr == 0) && (mask == 0)) {
+ sprintf(s, "default");
+ return (s);
+ }
+ a = (u_char *)&addr;
+ m = (u_char *)&mask;
+ bits = 33 - ffs(ntohl(mask));
+
+ if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3],
+ bits);
+ else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits);
+ else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits);
+ else sprintf(s, "%u/%d", a[0], bits);
+
+ return (s);
+}
+
+/*
+ * Convert the printable string representation of an IP address into the
+ * u_long (network) format. Return 0xffffffff on error. (To detect the
+ * legal address with that value, you must explicitly compare the string
+ * with "255.255.255.255".)
+ */
+u_int32
+inet_parse(s,n)
+ char *s;
+ int n;
+{
+ u_int32 a = 0;
+ u_int a0 = 0, a1 = 0, a2 = 0, a3 = 0;
+ int i;
+ char c;
+
+ i = sscanf(s, "%u.%u.%u.%u%c", &a0, &a1, &a2, &a3, &c);
+ if (i < n || i > 4 || a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
+ return (0xffffffff);
+
+ ((u_char *)&a)[0] = a0;
+ ((u_char *)&a)[1] = a1;
+ ((u_char *)&a)[2] = a2;
+ ((u_char *)&a)[3] = a3;
+
+ return (a);
+}
+
+
+/*
+ * inet_cksum extracted from:
+ * P I N G . C
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ * Modified at Uc Berkeley
+ *
+ * (ping.c) Status -
+ * Public Domain. Distribution Unlimited.
+ *
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+int
+inet_cksum(addr, len)
+ u_short *addr;
+ u_int len;
+{
+ register int nleft = (int)len;
+ register u_short *w = addr;
+ u_short answer = 0;
+ 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) {
+ *(u_char *) (&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /*
+ * 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/mrouted/ipip.c b/usr.sbin/mrouted/ipip.c
new file mode 100644
index 0000000..6e88d93
--- /dev/null
+++ b/usr.sbin/mrouted/ipip.c
@@ -0,0 +1,145 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * ipip.c,v 3.8.4.6 1998/01/06 01:57:45 fenner Exp
+ */
+
+
+#include "defs.h"
+
+#ifndef lint
+static char rcsid[] = "@(#) $Id: \
+ipip.c,v 3.8.4.6 1998/01/06 01:57:45 fenner Exp $";
+#endif
+
+/*
+ * Exported variables.
+ */
+#ifdef notyet
+int raw_socket; /* socket for raw network I/O */
+#endif
+/*
+ *XXX For now, we just use the IGMP socket to send packets.
+ * This is legal in BSD, because the protocol # is not checked
+ * on raw sockets. The k_* interfaces need to gain a socket
+ * argument so that we can call them on the raw_socket also.
+ */
+#define raw_socket igmp_socket
+
+/*
+ * Private variables.
+ */
+static int rawid = 0;
+
+/*
+ * Open and initialize the raw socket.
+ */
+void
+init_ipip()
+{
+#ifdef notyet
+ if ((raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+ log(LOG_ERR, errno, "Raw IP socket");
+#endif
+}
+
+/*
+ * Allocate and fill in static IP header for encapsulating on a tunnel.
+ */
+void
+init_ipip_on_vif(v)
+ struct uvif *v;
+{
+ struct ip *ip;
+
+ ip = v->uv_encap_hdr = (struct ip *)malloc(sizeof(struct ip));
+ if (ip == NULL)
+ log(LOG_ERR, 0, "out of memory");
+ bzero(ip, sizeof(struct ip));
+ /*
+ * Fields zeroed that aren't filled in later:
+ * - IP ID (let the kernel fill it in)
+ * - Offset (we don't send fragments)
+ * - Checksum (let the kernel fill it in)
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_tos = 0xc0; /* Internet Control */
+ ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+ ip->ip_p = IPPROTO_IPIP;
+ ip->ip_src.s_addr = v->uv_lcl_addr;
+ ip->ip_dst.s_addr = v->uv_rmt_addr;
+}
+
+/*
+ * Call build_igmp() to build an IGMP message in the output packet buffer.
+ * Then fill in the fields of the IP packet that build_igmp() left for the
+ * kernel to fill in, and encapsulate the original packet with the
+ * pre-created ip header for this vif.
+ */
+void
+send_ipip(src, dst, type, code, group, datalen, v)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+ struct uvif *v;
+{
+ struct msghdr msg;
+ struct iovec iov[2];
+ struct sockaddr_in sdst;
+ struct ip *ip;
+
+ build_igmp(src, dst, type, code, group, datalen);
+ ip = (struct ip *)send_buf;
+#ifndef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+ ip->ip_id = htons(rawid++);
+ ip->ip_sum = 0;
+ ip->ip_sum = inet_cksum((u_short *)ip, ip->ip_hl << 2);
+
+ ip = v->uv_encap_hdr;
+ ip->ip_len = 2 * MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ sdst.sin_len = sizeof(sdst);
+#endif
+ sdst.sin_addr = ip->ip_dst;
+
+ iov[0].iov_base = (caddr_t)v->uv_encap_hdr;
+ iov[0].iov_len = sizeof(struct ip);
+ iov[1].iov_base = (caddr_t)send_buf;
+ iov[1].iov_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+
+ bzero(&msg, sizeof(msg));
+ msg.msg_name = (caddr_t)&sdst;
+ msg.msg_namelen = sizeof(sdst);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ if (sendmsg(raw_socket, &msg, 0) < 0) {
+ if (errno == ENETDOWN)
+ check_vif_state();
+ else
+ log(LOG_WARNING, errno,
+ "sendmsg to %s on %s",
+ inet_fmt(sdst.sin_addr.s_addr, s1), inet_fmt(src, s2));
+ }
+
+ IF_DEBUG(DEBUG_PKT|igmp_debug_kind(type, code))
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s encaped to %s",
+ igmp_packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
+ inet_fmt(src, s1), inet_fmt(dst, s2),
+ inet_fmt(sdst.sin_addr.s_addr, s3));
+}
diff --git a/usr.sbin/mrouted/kern.c b/usr.sbin/mrouted/kern.c
new file mode 100644
index 0000000..716b479
--- /dev/null
+++ b/usr.sbin/mrouted/kern.c
@@ -0,0 +1,344 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * kern.c,v 3.8.4.10 1998/01/06 02:00:51 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+int curttl = 0;
+
+void k_set_rcvbuf(bufsize, minsize)
+ int bufsize;
+ int minsize;
+{
+ int delta = bufsize / 2;
+ int iter = 0;
+
+ /*
+ * Set the socket buffer. If we can't set it as large as we
+ * want, search around to try to find the highest acceptable
+ * value. The highest acceptable value being smaller than
+ * minsize is a fatal error.
+ */
+ if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ while (1) {
+ iter++;
+ if (delta > 1)
+ delta /= 2;
+
+ if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0) {
+ bufsize -= delta;
+ } else {
+ if (delta < 1024)
+ break;
+ bufsize += delta;
+ }
+ }
+ if (bufsize < minsize) {
+ log(LOG_ERR, 0, "OS-allowed buffer size %u < app min %u",
+ bufsize, minsize);
+ /*NOTREACHED*/
+ }
+ }
+ IF_DEBUG(DEBUG_KERN)
+ log(LOG_DEBUG, 0, "Got %d byte buffer size in %d iterations",
+ bufsize, iter);
+}
+
+
+void k_hdr_include(bool)
+ int bool;
+{
+#ifdef IP_HDRINCL
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&bool, sizeof(bool)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
+#endif
+}
+
+
+void k_set_ttl(t)
+ int t;
+{
+#ifndef RAW_OUTPUT_IS_RAW
+ u_char ttl;
+
+ ttl = t;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);
+#endif
+ curttl = t;
+}
+
+
+void k_set_loop(l)
+ int l;
+{
+ u_char loop;
+
+ loop = l;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
+}
+
+
+void k_set_if(ifa)
+ u_int32 ifa;
+{
+ struct in_addr adr;
+
+ adr.s_addr = ifa;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&adr, sizeof(adr)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
+ inet_fmt(ifa, s1));
+}
+
+
+void k_join(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't join group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+
+void k_leave(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't leave group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+
+void k_init_dvmrp()
+{
+#ifdef OLD_KERNEL
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT,
+ (char *)NULL, 0) < 0)
+#else
+ int v=1;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_INIT,
+ (char *)&v, sizeof(int)) < 0)
+#endif
+ log(LOG_ERR, errno, "can't enable Multicast routing in kernel");
+}
+
+
+void k_stop_dvmrp()
+{
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DONE,
+ (char *)NULL, 0) < 0)
+ log(LOG_WARNING, errno, "can't disable Multicast routing in kernel");
+}
+
+
+void k_add_vif(vifi, v)
+ vifi_t vifi;
+ struct uvif *v;
+{
+ struct vifctl vc;
+
+ vc.vifc_vifi = vifi;
+ vc.vifc_flags = v->uv_flags & VIFF_KERNEL_FLAGS;
+ vc.vifc_threshold = v->uv_threshold;
+ vc.vifc_rate_limit = v->uv_rate_limit;
+ vc.vifc_lcl_addr.s_addr = v->uv_lcl_addr;
+ vc.vifc_rmt_addr.s_addr = v->uv_rmt_addr;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_VIF,
+ (char *)&vc, sizeof(vc)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT_ADD_VIF on vif %d", vifi);
+}
+
+
+void k_del_vif(vifi)
+ vifi_t vifi;
+{
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_VIF,
+ (char *)&vifi, sizeof(vifi)) < 0)
+ log(LOG_ERR, errno, "setsockopt MRT_DEL_VIF on vif %d", vifi);
+}
+
+
+/*
+ * Adds a (source, mcastgrp) entry to the kernel
+ */
+void k_add_rg(origin, g)
+ u_int32 origin;
+ struct gtable *g;
+{
+ struct mfcctl mc;
+ vifi_t i;
+
+#ifdef DEBUG_MFC
+ md_log(MD_ADD, origin, g->gt_mcastgrp);
+#endif
+ /* copy table values so that setsockopt can process it */
+ mc.mfcc_origin.s_addr = origin;
+#ifdef OLD_KERNEL
+ mc.mfcc_originmask.s_addr = 0xffffffff;
+#endif
+ mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp;
+ mc.mfcc_parent = g->gt_route ? g->gt_route->rt_parent : NO_VIF;
+ for (i = 0; i < numvifs; i++)
+ mc.mfcc_ttls[i] = g->gt_ttls[i];
+
+ /* write to kernel space */
+ if (setsockopt(igmp_socket, IPPROTO_IP, MRT_ADD_MFC,
+ (char *)&mc, sizeof(mc)) < 0) {
+#ifdef DEBUG_MFC
+ md_log(MD_ADD_FAIL, origin, g->gt_mcastgrp);
+#endif
+ log(LOG_WARNING, errno, "setsockopt MRT_ADD_MFC",
+ inet_fmt(origin, s1), inet_fmt(g->gt_mcastgrp, s2));
+ }
+}
+
+
+/*
+ * Deletes a (source, mcastgrp) entry from the kernel
+ */
+int k_del_rg(origin, g)
+ u_int32 origin;
+ struct gtable *g;
+{
+ struct mfcctl mc;
+ int retval;
+
+#ifdef DEBUG_MFC
+ md_log(MD_DEL, origin, g->gt_mcastgrp);
+#endif
+ /* copy table values so that setsockopt can process it */
+ mc.mfcc_origin.s_addr = origin;
+#ifdef OLD_KERNEL
+ mc.mfcc_originmask.s_addr = 0xffffffff;
+#endif
+ mc.mfcc_mcastgrp.s_addr = g->gt_mcastgrp;
+
+ /* write to kernel space */
+ if ((retval = setsockopt(igmp_socket, IPPROTO_IP, MRT_DEL_MFC,
+ (char *)&mc, sizeof(mc))) < 0) {
+#ifdef DEBUG_MFC
+ md_log(MD_DEL_FAIL, origin, g->gt_mcastgrp);
+#endif
+ log(LOG_WARNING, errno, "setsockopt MRT_DEL_MFC of (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(g->gt_mcastgrp, s2));
+ }
+
+ return retval;
+}
+
+/*
+ * Get the kernel's idea of what version of mrouted needs to run with it.
+ */
+int k_get_version()
+{
+#ifdef OLD_KERNEL
+ return -1;
+#else
+ int vers;
+ int len = sizeof(vers);
+
+ if (getsockopt(igmp_socket, IPPROTO_IP, MRT_VERSION,
+ (char *)&vers, &len) < 0)
+ log(LOG_ERR, errno,
+ "getsockopt MRT_VERSION: perhaps your kernel is too old");
+
+ return vers;
+#endif
+}
+
+#if 0
+/*
+ * Get packet counters
+ */
+int
+k_get_vif_count(vifi, icount, ocount, ibytes, obytes)
+ vifi_t vifi;
+ int *icount, *ocount, *ibytes, *obytes;
+{
+ struct sioc_vif_req vreq;
+ int retval = 0;
+
+ vreq.vifi = vifi;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&vreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETVIFCNT on vif %d", vifi);
+ vreq.icount = vreq.ocount = vreq.ibytes =
+ vreq.obytes = 0xffffffff;
+ retval = 1;
+ }
+ if (icount)
+ *icount = vreq.icount;
+ if (ocount)
+ *ocount = vreq.ocount;
+ if (ibytes)
+ *ibytes = vreq.ibytes;
+ if (obytes)
+ *obytes = vreq.obytes;
+ return retval;
+}
+
+/*
+ * Get counters for a desired source and group.
+ */
+int
+k_get_sg_count(src, grp, pktcnt, bytecnt, wrong_if)
+ u_int32 src;
+ u_int32 grp;
+ struct sg_count *retval;
+{
+ struct sioc_sg_req sgreq;
+ int retval = 0;
+
+ sgreq.src.s_addr = src;
+ sgreq.grp.s_addr = grp;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sgreq) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
+ inet_fmt(src, s1), inet_fmt(grp, s2));
+ sgreq.pktcnt = sgreq.bytecnt = sgreq.wrong_if = 0xffffffff;
+ return 1;
+ }
+ if (pktcnt)
+ *pktcnt = sgreq.pktcnt;
+ if (bytecnt)
+ *bytecnt = sgreq.bytecnt;
+ if (wrong_if)
+ *wrong_if = sgreq.wrong_if;
+ return retval;
+}
+#endif
diff --git a/usr.sbin/mrouted/main.c b/usr.sbin/mrouted/main.c
new file mode 100644
index 0000000..72c52fe
--- /dev/null
+++ b/usr.sbin/mrouted/main.c
@@ -0,0 +1,1064 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * main.c,v 3.8.4.29 1998/03/01 01:49:00 fenner Exp
+ */
+
+/*
+ * Written by Steve Deering, Stanford University, February 1989.
+ *
+ * (An earlier version of DVMRP was implemented by David Waitzman of
+ * BBN STC by extending Berkeley's routed program. Some of Waitzman's
+ * extensions have been incorporated into mrouted, but none of the
+ * original routed code has been adopted.)
+ */
+
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#include <err.h>
+#include "defs.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <fcntl.h>
+#include <paths.h>
+
+#ifdef SNMP
+#include "snmp.h"
+#endif
+
+extern char *configfilename;
+char versionstring[100];
+
+static char pidfilename[] = _PATH_MROUTED_PID;
+static char dumpfilename[] = _PATH_MROUTED_DUMP;
+static char cachefilename[] = _PATH_MROUTED_CACHE;
+static char genidfilename[] = _PATH_MROUTED_GENID;
+
+static int haveterminal = 1;
+int did_final_init = 0;
+
+static int sighandled = 0;
+#define GOT_SIGINT 0x01
+#define GOT_SIGHUP 0x02
+#define GOT_SIGUSR1 0x04
+#define GOT_SIGUSR2 0x08
+
+int cache_lifetime = DEFAULT_CACHE_LIFETIME;
+int prune_lifetime = AVERAGE_PRUNE_LIFETIME;
+
+int debug = 0;
+char *progname;
+time_t mrouted_init_time;
+
+#ifdef SNMP
+#define NHANDLERS 34
+#else
+#define NHANDLERS 2
+#endif
+
+static struct ihandler {
+ int fd; /* File descriptor */
+ ihfunc_t func; /* Function to call with &fd_set */
+} ihandlers[NHANDLERS];
+static int nhandlers = 0;
+
+static struct debugname {
+ char *name;
+ int level;
+ int nchars;
+} debugnames[] = {
+ { "packet", DEBUG_PKT, 2 },
+ { "pkt", DEBUG_PKT, 3 },
+ { "pruning", DEBUG_PRUNE, 1 },
+ { "prunes", DEBUG_PRUNE, 1 },
+ { "routing", DEBUG_ROUTE, 1 },
+ { "routes", DEBUG_ROUTE, 1 },
+ { "route_detail", DEBUG_RTDETAIL, 6 },
+ { "rtdetail", DEBUG_RTDETAIL, 2 },
+ { "peers", DEBUG_PEER, 2 },
+ { "neighbors", DEBUG_PEER, 1 },
+ { "cache", DEBUG_CACHE, 1 },
+ { "timeout", DEBUG_TIMEOUT, 1 },
+ { "callout", DEBUG_TIMEOUT, 2 },
+ { "interface", DEBUG_IF, 2 },
+ { "vif", DEBUG_IF, 1 },
+ { "membership", DEBUG_MEMBER, 1 },
+ { "groups", DEBUG_MEMBER, 1 },
+ { "traceroute", DEBUG_TRACE, 2 },
+ { "mtrace", DEBUG_TRACE, 2 },
+ { "igmp", DEBUG_IGMP, 1 },
+ { "icmp", DEBUG_ICMP, 2 },
+ { "rsrr", DEBUG_RSRR, 2 },
+ { "3", 0xffffffff, 1 } /* compat. */
+};
+
+/*
+ * Forward declarations.
+ */
+static void final_init __P((void *));
+static void fasttimer __P((void *));
+static void timer __P((void *));
+static void dump __P((void));
+static void dump_version __P((FILE *));
+static void fdump __P((void));
+static void cdump __P((void));
+static void restart __P((void));
+static void handler __P((int));
+static void cleanup __P((void));
+static void resetlogging __P((void *));
+static void usage __P((void));
+
+/* To shut up gcc -Wstrict-prototypes */
+int main __P((int argc, char **argv));
+
+int
+register_input_handler(fd, func)
+ int fd;
+ ihfunc_t func;
+{
+ if (nhandlers >= NHANDLERS)
+ return -1;
+
+ ihandlers[nhandlers].fd = fd;
+ ihandlers[nhandlers++].func = func;
+
+ return 0;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int recvlen;
+ int dummy;
+ FILE *fp;
+ struct timeval tv, difftime, curtime, lasttime, *timeout;
+ u_int32 prev_genid;
+ int vers;
+ fd_set rfds, readers;
+ int nfds, n, i, secs;
+ extern char todaysversion[];
+ struct sigaction sa;
+#ifdef SNMP
+ struct timeval timeout, *tvp = &timeout;
+ struct timeval sched, *svp = &sched, now, *nvp = &now;
+ int index, block;
+#endif
+
+ setlinebuf(stderr);
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ argv++, argc--;
+ while (argc > 0 && *argv[0] == '-') {
+ if (strcmp(*argv, "-d") == 0) {
+ if (argc > 1 && *(argv + 1)[0] != '-') {
+ char *p,*q;
+ int i, len;
+ struct debugname *d;
+
+ argv++, argc--;
+ debug = 0;
+ p = *argv; q = NULL;
+ while (p) {
+ q = strchr(p, ',');
+ if (q)
+ *q++ = '\0';
+ len = strlen(p);
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++)
+ if (len >= d->nchars && strncmp(d->name, p, len) == 0)
+ break;
+ if (i == sizeof(debugnames) / sizeof(debugnames[0])) {
+ int j = 0xffffffff;
+ int k = 0;
+ fprintf(stderr, "Valid debug levels: ");
+ for (i = 0, d = debugnames;
+ i < sizeof(debugnames) / sizeof(debugnames[0]);
+ i++, d++) {
+ if ((j & d->level) == d->level) {
+ if (k++)
+ putc(',', stderr);
+ fputs(d->name, stderr);
+ j &= ~d->level;
+ }
+ }
+ putc('\n', stderr);
+ usage();
+ }
+ debug |= d->level;
+ p = q;
+ }
+ } else
+ debug = DEFAULT_DEBUG;
+ } else if (strcmp(*argv, "-c") == 0) {
+ if (argc > 1) {
+ argv++, argc--;
+ configfilename = *argv;
+ } else
+ usage();
+ } else if (strcmp(*argv, "-p") == 0) {
+ log(LOG_WARNING, 0, "disabling pruning is no longer supported");
+#ifdef SNMP
+ } else if (strcmp(*argv, "-P") == 0) {
+ if (argc > 1 && isdigit(*(argv + 1)[0])) {
+ argv++, argc--;
+ dest_port = atoi(*argv);
+ } else
+ dest_port = DEFAULT_PORT;
+#endif
+ } else
+ usage();
+ argv++, argc--;
+ }
+
+ if (argc > 0)
+ usage();
+
+ if (debug != 0) {
+ struct debugname *d;
+ char c;
+ int tmpd = debug;
+
+ fprintf(stderr, "debug level 0x%x ", debug);
+ c = '(';
+ for (d = debugnames; d < debugnames +
+ sizeof(debugnames) / sizeof(debugnames[0]); d++) {
+ if ((tmpd & d->level) == d->level) {
+ tmpd &= ~d->level;
+ fprintf(stderr, "%c%s", c, d->name);
+ c = ',';
+ }
+ }
+ fprintf(stderr, ")\n");
+ }
+
+#ifdef LOG_DAEMON
+ (void)openlog("mrouted", LOG_PID, LOG_DAEMON);
+ (void)setlogmask(LOG_UPTO(LOG_NOTICE));
+#else
+ (void)openlog("mrouted", LOG_PID);
+#endif
+ sprintf(versionstring, "mrouted version %s", todaysversion);
+
+ log(LOG_DEBUG, 0, "%s starting", versionstring);
+
+#ifdef SYSV
+ srand48(time(NULL));
+#endif
+
+ /*
+ * Get generation id
+ */
+ gettimeofday(&tv, 0);
+ dvmrp_genid = tv.tv_sec;
+
+ fp = fopen(genidfilename, "r");
+ if (fp != NULL) {
+ fscanf(fp, "%d", &prev_genid);
+ if (prev_genid == dvmrp_genid)
+ dvmrp_genid++;
+ (void) fclose(fp);
+ }
+
+ fp = fopen(genidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d", dvmrp_genid);
+ (void) fclose(fp);
+ }
+
+ /* Start up the log rate-limiter */
+ resetlogging(NULL);
+
+ callout_init();
+ init_igmp();
+ init_icmp();
+ init_ipip();
+ init_routes();
+ init_ktable();
+#ifndef OLD_KERNEL
+ /*
+ * Unfortunately, you can't k_get_version() unless you've
+ * k_init_dvmrp()'d. Now that we want to move the
+ * k_init_dvmrp() to later in the initialization sequence,
+ * we have to do the disgusting hack of initializing,
+ * getting the version, then stopping the kernel multicast
+ * forwarding.
+ */
+ k_init_dvmrp();
+ vers = k_get_version();
+ k_stop_dvmrp();
+ /*XXX
+ * This function must change whenever the kernel version changes
+ */
+ if ((((vers >> 8) & 0xff) != 3) ||
+ ((vers & 0xff) != 5))
+ log(LOG_ERR, 0, "kernel (v%d.%d)/mrouted (v%d.%d) version mismatch",
+ (vers >> 8) & 0xff, vers & 0xff,
+ PROTOCOL_VERSION, MROUTED_VERSION);
+#endif
+
+#ifdef SNMP
+ if (i = snmp_init())
+ return i;
+
+ gettimeofday(nvp, 0);
+ if (nvp->tv_usec < 500000L){
+ svp->tv_usec = nvp->tv_usec + 500000L;
+ svp->tv_sec = nvp->tv_sec;
+ } else {
+ svp->tv_usec = nvp->tv_usec - 500000L;
+ svp->tv_sec = nvp->tv_sec + 1;
+ }
+#endif /* SNMP */
+
+ init_vifs();
+
+#ifdef RSRR
+ rsrr_init();
+#endif /* RSRR */
+
+ sa.sa_handler = handler;
+ sa.sa_flags = 0; /* Interrupt system calls */
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGUSR2, &sa, NULL);
+
+ if (igmp_socket >= FD_SETSIZE)
+ log(LOG_ERR, 0, "descriptor too big");
+ FD_ZERO(&readers);
+ FD_SET(igmp_socket, &readers);
+ nfds = igmp_socket + 1;
+ for (i = 0; i < nhandlers; i++) {
+ if (ihandlers[i].fd >= FD_SETSIZE)
+ log(LOG_ERR, 0, "descriptor too big");
+ FD_SET(ihandlers[i].fd, &readers);
+ if (ihandlers[i].fd >= nfds)
+ nfds = ihandlers[i].fd + 1;
+ }
+
+ IF_DEBUG(DEBUG_IF)
+ dump_vifs(stderr);
+ IF_DEBUG(DEBUG_ROUTE)
+ dump_routes(stderr);
+
+ /* schedule first timer interrupt */
+ timer_setTimer(1, fasttimer, NULL);
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+
+ if (debug == 0) {
+ /*
+ * Detach from the terminal
+ */
+ int t;
+
+ haveterminal = 0;
+ if (fork()) exit(0);
+ (void)close(0);
+ (void)close(1);
+ (void)close(2);
+ (void)open("/", 0);
+ (void)dup2(0, 1);
+ (void)dup2(0, 2);
+#if defined(SYSV) || defined(linux)
+ (void)setpgrp();
+#else
+#ifdef TIOCNOTTY
+ t = open(_PATH_TTY, 2);
+ if (t >= 0) {
+ (void)ioctl(t, TIOCNOTTY, (char *)0);
+ (void)close(t);
+ }
+#else
+ if (setsid() < 0)
+ perror("setsid");
+#endif
+#endif
+ }
+
+ fp = fopen(pidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", (int)getpid());
+ (void) fclose(fp);
+ }
+
+ /* XXX HACK
+ * This will cause black holes for the first few seconds after startup,
+ * since we are exchanging routes but not actually forwarding.
+ * However, it eliminates much of the startup transient.
+ *
+ * It's possible that we can set a flag which says not to report any
+ * routes (just accept reports) until this timer fires, and then
+ * do a report_to_all_neighbors(ALL_ROUTES) immediately before
+ * turning on DVMRP.
+ */
+ timer_setTimer(10, final_init, NULL);
+
+ /*
+ * Main receive loop.
+ */
+ dummy = 0;
+ difftime.tv_usec = 0;
+ gettimeofday(&curtime, NULL);
+ lasttime = curtime;
+ for(;;) {
+ bcopy((char *)&readers, (char *)&rfds, sizeof(rfds));
+ secs = timer_nextTimer();
+ if (secs == -1)
+ timeout = NULL;
+ else {
+ timeout = &tv;
+ timeout->tv_sec = secs;
+ timeout->tv_usec = 0;
+ }
+#ifdef SNMP
+ THIS IS BROKEN
+ if (nvp->tv_sec > svp->tv_sec
+ || (nvp->tv_sec == svp->tv_sec && nvp->tv_usec > svp->tv_usec)){
+ alarmTimer(nvp);
+ eventTimer(nvp);
+ if (nvp->tv_usec < 500000L){
+ svp->tv_usec = nvp->tv_usec + 500000L;
+ svp->tv_sec = nvp->tv_sec;
+ } else {
+ svp->tv_usec = nvp->tv_usec - 500000L;
+ svp->tv_sec = nvp->tv_sec + 1;
+ }
+ }
+
+ tvp = &timeout;
+ tvp->tv_sec = 0;
+ tvp->tv_usec = 500000L;
+
+ block = 0;
+ snmp_select_info(&nfds, &rfds, tvp, &block);
+ if (block == 1)
+ tvp = NULL; /* block without timeout */
+ if ((n = select(nfds, &rfds, NULL, NULL, tvp)) < 0)
+#endif
+ if (sighandled) {
+ if (sighandled & GOT_SIGINT) {
+ sighandled &= ~GOT_SIGINT;
+ break;
+ }
+ if (sighandled & GOT_SIGHUP) {
+ sighandled &= ~GOT_SIGHUP;
+ restart();
+ }
+ if (sighandled & GOT_SIGUSR1) {
+ sighandled &= ~GOT_SIGUSR1;
+ fdump();
+ }
+ if (sighandled & GOT_SIGUSR2) {
+ sighandled &= ~GOT_SIGUSR2;
+ cdump();
+ }
+ }
+ if ((n = select(nfds, &rfds, NULL, NULL, timeout)) < 0) {
+ if (errno != EINTR)
+ log(LOG_WARNING, errno, "select failed");
+ continue;
+ }
+
+ if (n > 0) {
+ if (FD_ISSET(igmp_socket, &rfds)) {
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen < 0) {
+ if (errno != EINTR) log(LOG_ERR, errno, "recvfrom");
+ continue;
+ }
+ accept_igmp(recvlen);
+ }
+
+ for (i = 0; i < nhandlers; i++) {
+ if (FD_ISSET(ihandlers[i].fd, &rfds)) {
+ (*ihandlers[i].func)(ihandlers[i].fd, &rfds);
+ }
+ }
+ }
+
+#ifdef SNMP
+ THIS IS BROKEN
+ snmp_read(&rfds);
+ snmp_timeout(); /* poll */
+#endif
+ /*
+ * Handle timeout queue.
+ *
+ * If select + packet processing took more than 1 second,
+ * or if there is a timeout pending, age the timeout queue.
+ *
+ * If not, collect usec in difftime to make sure that the
+ * time doesn't drift too badly.
+ *
+ * If the timeout handlers took more than 1 second,
+ * age the timeout queue again. XXX This introduces the
+ * potential for infinite loops!
+ */
+ do {
+ /*
+ * If the select timed out, then there's no other
+ * activity to account for and we don't need to
+ * call gettimeofday.
+ */
+ if (n == 0) {
+ curtime.tv_sec = lasttime.tv_sec + secs;
+ curtime.tv_usec = lasttime.tv_usec;
+ n = -1; /* don't do this next time through the loop */
+ } else
+ gettimeofday(&curtime, NULL);
+ difftime.tv_sec = curtime.tv_sec - lasttime.tv_sec;
+ difftime.tv_usec += curtime.tv_usec - lasttime.tv_usec;
+ while (difftime.tv_usec >= 1000000) {
+ difftime.tv_sec++;
+ difftime.tv_usec -= 1000000;
+ }
+ if (difftime.tv_usec < 0) {
+ difftime.tv_sec--;
+ difftime.tv_usec += 1000000;
+ }
+ lasttime = curtime;
+ if (secs == 0 || difftime.tv_sec > 0)
+ age_callout_queue(difftime.tv_sec);
+ secs = -1;
+ } while (difftime.tv_sec > 0);
+ }
+ log(LOG_NOTICE, 0, "%s exiting", versionstring);
+ cleanup();
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
+ exit(1);
+}
+
+static void
+final_init(i)
+ void *i;
+{
+ char *s = (char *)i;
+
+ log(LOG_NOTICE, 0, "%s%s", versionstring, s ? s : "");
+ if (s)
+ free(s);
+
+ k_init_dvmrp(); /* enable DVMRP routing in kernel */
+
+ /*
+ * Install the vifs in the kernel as late as possible in the
+ * initialization sequence.
+ */
+ init_installvifs();
+
+ time(&mrouted_init_time);
+ did_final_init = 1;
+}
+
+/*
+ * routine invoked every second. Its main goal is to cycle through
+ * the routing table and send partial updates to all neighbors at a
+ * rate that will cause the entire table to be sent in ROUTE_REPORT_INTERVAL
+ * seconds. Also, every TIMER_INTERVAL seconds it calls timer() to
+ * do all the other time-based processing.
+ */
+static void
+fasttimer(i)
+ void *i;
+{
+ static unsigned int tlast;
+ static unsigned int nsent;
+ register unsigned int t = tlast + 1;
+ register int n;
+
+ /*
+ * if we're in the last second, send everything that's left.
+ * otherwise send at least the fraction we should have sent by now.
+ */
+ if (t >= ROUTE_REPORT_INTERVAL) {
+ register int nleft = nroutes - nsent;
+ while (nleft > 0) {
+ if ((n = report_next_chunk()) <= 0)
+ break;
+ nleft -= n;
+ }
+ tlast = 0;
+ nsent = 0;
+ } else {
+ register unsigned int ncum = nroutes * t / ROUTE_REPORT_INTERVAL;
+ while (nsent < ncum) {
+ if ((n = report_next_chunk()) <= 0)
+ break;
+ nsent += n;
+ }
+ tlast = t;
+ }
+
+ timer_setTimer(1, fasttimer, NULL);
+}
+
+/*
+ * The 'virtual_time' variable is initialized to a value that will cause the
+ * first invocation of timer() to send a probe or route report to all vifs
+ * and send group membership queries to all subnets for which this router is
+ * querier. This first invocation occurs approximately TIMER_INTERVAL seconds
+ * after the router starts up. Note that probes for neighbors and queries
+ * for group memberships are also sent at start-up time, as part of initial-
+ * ization. This repetition after a short interval is desirable for quickly
+ * building up topology and membership information in the presence of possible
+ * packet loss.
+ *
+ * 'virtual_time' advances at a rate that is only a crude approximation of
+ * real time, because it does not take into account any time spent processing,
+ * and because the timer intervals are sometimes shrunk by a random amount to
+ * avoid unwanted synchronization with other routers.
+ */
+
+u_long virtual_time = 0;
+
+
+/*
+ * Timer routine. Performs periodic neighbor probing, route reporting, and
+ * group querying duties, and drives various timers in routing entries and
+ * virtual interface data structures.
+ */
+static void
+timer(i)
+ void *i;
+{
+ age_routes(); /* Advance the timers in the route entries */
+ age_vifs(); /* Advance the timers for neighbors */
+ age_table_entry(); /* Advance the timers for the cache entries */
+
+ if (virtual_time % IGMP_QUERY_INTERVAL == 0) {
+ /*
+ * Time to query the local group memberships on all subnets
+ * for which this router is the elected querier.
+ */
+ query_groups();
+ }
+
+ if (virtual_time % NEIGHBOR_PROBE_INTERVAL == 0) {
+ /*
+ * Time to send a probe on all vifs from which no neighbors have
+ * been heard. Also, check if any inoperative interfaces have now
+ * come up. (If they have, they will also be probed as part of
+ * their initialization.)
+ */
+ probe_for_neighbors();
+
+ if (vifs_down)
+ check_vif_state();
+ }
+
+ delay_change_reports = FALSE;
+ if (routes_changed) {
+ /*
+ * Some routes have changed since the last timer interrupt, but
+ * have not been reported yet. Report the changed routes to all
+ * neighbors.
+ */
+ report_to_all_neighbors(CHANGED_ROUTES);
+ }
+
+#ifdef SNMP
+ sync_timer();
+#endif
+
+ /*
+ * Advance virtual time
+ */
+ virtual_time += TIMER_INTERVAL;
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+}
+
+
+static void
+cleanup()
+{
+ static in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup++;
+#ifdef RSRR
+ rsrr_clean();
+#endif /* RSRR */
+ expire_all_routes();
+ report_to_all_neighbors(ALL_ROUTES);
+ if (did_final_init)
+ k_stop_dvmrp();
+ }
+}
+
+/*
+ * Signal handler. Take note of the fact that the signal arrived
+ * so that the main loop can take care of it.
+ */
+static void
+handler(sig)
+ int sig;
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ sighandled |= GOT_SIGINT;
+ break;
+
+ case SIGHUP:
+ sighandled |= GOT_SIGHUP;
+ break;
+
+ case SIGUSR1:
+ sighandled |= GOT_SIGUSR1;
+ break;
+
+ case SIGUSR2:
+ sighandled |= GOT_SIGUSR2;
+ break;
+ }
+}
+
+/*
+ * Dump internal data structures to stderr.
+ */
+static void
+dump()
+{
+ dump_vifs(stderr);
+ dump_routes(stderr);
+}
+
+static void
+dump_version(fp)
+ FILE *fp;
+{
+ time_t t;
+
+ time(&t);
+ fprintf(fp, "%s ", versionstring);
+ if (did_final_init)
+ fprintf(fp, "up %s",
+ scaletime(t - mrouted_init_time));
+ else
+ fprintf(fp, "(not yet initialized)");
+ fprintf(fp, " %s\n", ctime(&t));
+}
+
+/*
+ * Dump internal data structures to a file.
+ */
+static void
+fdump()
+{
+ FILE *fp;
+
+ fp = fopen(dumpfilename, "w");
+ if (fp != NULL) {
+ dump_version(fp);
+ dump_vifs(fp);
+ dump_routes(fp);
+ (void) fclose(fp);
+ }
+}
+
+
+/*
+ * Dump local cache contents to a file.
+ */
+static void
+cdump()
+{
+ FILE *fp;
+
+ fp = fopen(cachefilename, "w");
+ if (fp != NULL) {
+ dump_version(fp);
+ dump_cache(fp);
+ (void) fclose(fp);
+ }
+}
+
+
+/*
+ * Restart mrouted
+ */
+static void
+restart()
+{
+ char *s;
+
+ s = (char *)malloc(sizeof(" restart"));
+ if (s == NULL)
+ log(LOG_ERR, 0, "out of memory");
+ strcpy(s, " restart");
+
+ /*
+ * reset all the entries
+ */
+ free_all_prunes();
+ free_all_routes();
+ free_all_callouts();
+ stop_all_vifs();
+ k_stop_dvmrp();
+ close(igmp_socket);
+ close(udp_socket);
+ did_final_init = 0;
+
+ /*
+ * start processing again
+ */
+ dvmrp_genid++;
+
+ init_igmp();
+ init_routes();
+ init_ktable();
+ init_vifs();
+ /*XXX Schedule final_init() as main does? */
+ final_init(s);
+
+ /* schedule timer interrupts */
+ timer_setTimer(1, fasttimer, NULL);
+ timer_setTimer(TIMER_INTERVAL, timer, NULL);
+}
+
+#define LOG_MAX_MSGS 20 /* if > 20/minute then shut up for a while */
+#define LOG_SHUT_UP 600 /* shut up for 10 minutes */
+static int log_nmsgs = 0;
+
+static void
+resetlogging(arg)
+ void *arg;
+{
+ int nxttime = 60;
+ void *narg = NULL;
+
+ if (arg == NULL && log_nmsgs > LOG_MAX_MSGS) {
+ nxttime = LOG_SHUT_UP;
+ narg = (void *)&log_nmsgs; /* just need some valid void * */
+ syslog(LOG_WARNING, "logging too fast, shutting up for %d minutes",
+ LOG_SHUT_UP / 60);
+ } else {
+ log_nmsgs = 0;
+ }
+
+ timer_setTimer(nxttime, resetlogging, narg);
+}
+
+char *
+scaletime(t)
+ u_long t;
+{
+#define SCALETIMEBUFLEN 20
+ static char buf1[20];
+ static char buf2[20];
+ static char *buf = buf1;
+ char *p;
+
+ p = buf;
+ if (buf == buf1)
+ buf = buf2;
+ else
+ buf = buf1;
+
+ /* XXX snprintf */
+ sprintf(p, "%2ld:%02ld:%02ld", t / 3600, (t % 3600) / 60, t % 60);
+ p[SCALETIMEBUFLEN - 1] = '\0';
+ return p;
+}
+
+#ifdef RINGBUFFER
+#define NLOGMSGS 10000
+#define LOGMSGSIZE 200
+char *logmsg[NLOGMSGS];
+static int logmsgno = 0;
+
+void
+printringbuf()
+{
+ FILE *f;
+ int i;
+
+ f = fopen("/var/tmp/mrouted.log", "a");
+ if (f == NULL) {
+ log(LOG_ERR, errno, "can't open /var/tmp/mrouted.log");
+ /*NOTREACHED*/
+ }
+ fprintf(f, "--------------------------------------------\n");
+
+ i = (logmsgno + 1) % NLOGMSGS;
+
+ while (i != logmsgno) {
+ if (*logmsg[i]) {
+ fprintf(f, "%s\n", logmsg[i]);
+ *logmsg[i] = '\0';
+ }
+ i = (i + 1) % NLOGMSGS;
+ }
+
+ fclose(f);
+}
+#endif
+
+/*
+ * Log errors and other messages to the system log daemon and to stderr,
+ * according to the severity of the message and the current debug level.
+ * For errors of severity LOG_ERR or worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ static char fmt[211] = "warning - ";
+ char *msg;
+ struct timeval now;
+ time_t now_sec;
+ struct tm *thyme;
+#ifdef RINGBUFFER
+ static int ringbufinit = 0;
+#endif
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ static char fmt[311] = "warning - ";
+ char *msg;
+ char tbuf[20];
+ struct timeval now;
+ time_t now_sec;
+ struct tm *thyme;
+#ifdef RINGBUFFER
+ static int ringbufinit = 0;
+#endif
+
+ va_start(ap);
+#endif
+ vsnprintf(&fmt[10], sizeof(fmt) - 10, format, ap);
+ va_end(ap);
+ msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
+
+#ifdef RINGBUFFER
+ if (!ringbufinit) {
+ int i;
+
+ for (i = 0; i < NLOGMSGS; i++) {
+ logmsg[i] = malloc(LOGMSGSIZE);
+ if (logmsg[i] == 0) {
+ syslog(LOG_ERR, "out of memory");
+ exit(-1);
+ }
+ *logmsg[i] = 0;
+ }
+ ringbufinit = 1;
+ }
+ gettimeofday(&now,NULL);
+ now_sec = now.tv_sec;
+ thyme = localtime(&now_sec);
+ snprintf(logmsg[logmsgno++], LOGMSGSIZE, "%02d:%02d:%02d.%03ld %s err %d",
+ thyme->tm_hour, thyme->tm_min, thyme->tm_sec,
+ now.tv_usec / 1000, msg, syserr);
+ logmsgno %= NLOGMSGS;
+ if (severity <= LOG_NOTICE)
+#endif
+ /*
+ * Log to stderr if we haven't forked yet and it's a warning or worse,
+ * or if we're debugging.
+ */
+ if (haveterminal && (debug || severity <= LOG_WARNING)) {
+ gettimeofday(&now,NULL);
+ now_sec = now.tv_sec;
+ thyme = localtime(&now_sec);
+ if (!debug)
+ fprintf(stderr, "%s: ", progname);
+ fprintf(stderr, "%02d:%02d:%02d.%03ld %s", thyme->tm_hour,
+ thyme->tm_min, thyme->tm_sec, now.tv_usec / 1000, msg);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+
+ /*
+ * Always log things that are worse than warnings, no matter what
+ * the log_nmsgs rate limiter says.
+ * Only count things worse than debugging in the rate limiter
+ * (since if you put daemon.debug in syslog.conf you probably
+ * actually want to log the debugging messages so they shouldn't
+ * be rate-limited)
+ */
+ if ((severity < LOG_WARNING) || (log_nmsgs < LOG_MAX_MSGS)) {
+ if (severity < LOG_DEBUG)
+ log_nmsgs++;
+ if (syserr != 0) {
+ errno = syserr;
+ syslog(severity, "%s: %m", msg);
+ } else
+ syslog(severity, "%s", msg);
+ }
+
+ if (severity <= LOG_ERR) exit(-1);
+}
+
+#ifdef DEBUG_MFC
+void
+md_log(what, origin, mcastgrp)
+ int what;
+ u_int32 origin, mcastgrp;
+{
+ static FILE *f = NULL;
+ struct timeval tv;
+ u_int32 buf[4];
+
+ if (!f) {
+ if ((f = fopen("/tmp/mrouted.clog", "w")) == NULL) {
+ log(LOG_ERR, errno, "open /tmp/mrouted.clog");
+ }
+ }
+
+ gettimeofday(&tv, NULL);
+ buf[0] = tv.tv_sec;
+ buf[1] = what;
+ buf[2] = origin;
+ buf[3] = mcastgrp;
+
+ fwrite(buf, sizeof(u_int32), 4, f);
+}
+#endif
diff --git a/usr.sbin/mrouted/map-mbone.8 b/usr.sbin/mrouted/map-mbone.8
new file mode 100644
index 0000000..a6e9e58
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone.8
@@ -0,0 +1,96 @@
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MAP-MBONE 8
+.Os
+.Sh NAME
+.Nm map-mbone
+.Nd multicast connection mapper
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar debug_level
+.Op Fl f
+.Op Fl g
+.Op Fl n
+.Op Fl r Ar retry_count
+.Op Fl t Ar timeout_count
+.Op Ar starting_router
+.Sh DESCRIPTION
+The
+.Nm
+utility
+attempts to display all multicast routers that are reachable from the multicast
+.Ar starting_router .
+If not specified on the command line, the default multicast
+.Ar starting_router
+is the localhost.
+.Pp
+The
+.Nm
+utility
+traverses neighboring multicast routers by sending the ASK_NEIGHBORS IGMP
+message to the multicast starting_router.
+If this multicast router responds,
+the version number and a list of their neighboring multicast router addresses is
+part of that response.
+If the responding router has recent multicast version
+number, then
+.Nm
+requests additional information such as metrics, thresholds, and flags from the
+multicast router.
+For each new occurrence of neighboring multicast router in
+the reply and provided the flooding option has been selected, then
+.Nm
+asks each of this multicast router for a list of neighbors.
+This search
+for unique routers will continue until no new neighboring multicast routers
+are reported.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Set the debug level.
+When the debug level is greater than the
+default value of 0, addition debugging messages are printed.
+Regardless of
+the debug level, an error condition, will always write an error message and will
+cause
+.Nm
+to terminate.
+Non-zero debug levels have the following effects:
+.Bl -tag -width indent
+.It "level 1"
+packet warnings are printed to stderr.
+.It "level 2"
+all level 1 messages plus notifications down networks are printed to stderr.
+.It "level 3"
+all level 2 messages plus notifications of all packet
+timeouts are printed to stderr.
+.El
+.It Fl f
+Set flooding option.
+Flooding allows the recursive search
+of neighboring multicast routers and is enable by default when starting_router
+is not used.
+.It Fl g
+Set graphing in GraphEd format.
+.It Fl n
+Disable the DNS lookup for the multicast routers names.
+.It Fl r Ar retry_count
+Set the neighbor query retry limit.
+Default is 1 retry.
+.It Fl t Ar timeout_count
+Set the number of seconds to wait for a neighbor query
+reply before retrying.
+Default timeout is 2 seconds.
+.El
+.Sh IMPORTANT NOTE
+The
+.Nm
+utility must be run as root.
+.Sh SEE ALSO
+.Xr mrinfo 8 ,
+.Xr mrouted 8 ,
+.Xr mtrace 8
+.Sh AUTHORS
+.An Pavel Curtis
diff --git a/usr.sbin/mrouted/map-mbone/Makefile b/usr.sbin/mrouted/map-mbone/Makefile
new file mode 100644
index 0000000..f99247b
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= map-mbone
+MAN= map-mbone.8
+SRCS= mapper.c
+
+CFLAGS+= -I$S
+
+DPADD= ${LIBMROUTED}
+LDADD= ${LIBMROUTED}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mapper.c b/usr.sbin/mrouted/mapper.c
new file mode 100644
index 0000000..8fdbad0
--- /dev/null
+++ b/usr.sbin/mrouted/mapper.c
@@ -0,0 +1,1048 @@
+/* Mapper for connections between MRouteD multicast routers.
+ * Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
+ *
+ * mapper.c,v 3.8.4.3 1998/01/06 01:57:47 fenner Exp
+ */
+
+/*
+ * Copyright (c) Xerox Corporation 1992. All rights reserved.
+ *
+ * License is granted to copy, to use, and to make and to use derivative
+ * works for research and evaluation purposes, provided that Xerox is
+ * acknowledged in all documentation pertaining to any such copy or derivative
+ * work. Xerox grants no other licenses expressed or implied. The Xerox trade
+ * name should not be used in any advertising without its written permission.
+ *
+ * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE
+ * FOR ANY PARTICULAR PURPOSE. The software is provided "as is" without
+ * express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+#include <arpa/inet.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define DEFAULT_TIMEOUT 2 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 1 /* How many times to ask each router */
+
+
+/* All IP addresses are stored in the data structure in NET order. */
+
+typedef struct neighbor {
+ struct neighbor *next;
+ u_int32 addr; /* IP address in NET order */
+ u_char metric; /* TTL cost of forwarding */
+ u_char threshold; /* TTL threshold to forward */
+ u_short flags; /* flags on connection */
+#define NF_PRESENT 0x8000 /* True if flags are meaningful */
+} Neighbor;
+
+typedef struct interface {
+ struct interface *next;
+ u_int32 addr; /* IP address of the interface in NET order */
+ Neighbor *neighbors; /* List of neighbors' IP addresses */
+} Interface;
+
+typedef struct node {
+ u_int32 addr; /* IP address of this entry in NET order */
+ u_int32 version; /* which mrouted version is running */
+ int tries; /* How many requests sent? -1 for aliases */
+ union {
+ struct node *alias; /* If alias, to what? */
+ struct interface *interfaces; /* Else, neighbor data */
+ } u;
+ struct node *left, *right;
+} Node;
+
+
+Node *routers = 0;
+u_int32 our_addr, target_addr = 0; /* in NET order */
+int debug = 0;
+int retries = DEFAULT_RETRIES;
+int timeout = DEFAULT_TIMEOUT;
+int show_names = TRUE;
+vifi_t numvifs; /* to keep loader happy */
+ /* (see COPY_TABLES macro called in kern.c) */
+
+Node * find_node __P((u_int32 addr, Node **ptr));
+Interface * find_interface __P((u_int32 addr, Node *node));
+Neighbor * find_neighbor __P((u_int32 addr, Node *node));
+int main __P((int argc, char *argv[]));
+void ask __P((u_int32 dst));
+void ask2 __P((u_int32 dst));
+int retry_requests __P((Node *node));
+char * inet_name __P((u_int32 addr));
+void print_map __P((Node *node));
+char * graph_name __P((u_int32 addr, char *buf, int len));
+void graph_edges __P((Node *node));
+void elide_aliases __P((Node *node));
+void graph_map __P((void));
+int get_number __P((int *var, int deflt, char ***pargv,
+ int *pargc));
+u_int32 host_addr __P((char *name));
+static void usage __P((void));
+
+
+Node *find_node(addr, ptr)
+ u_int32 addr;
+ Node **ptr;
+{
+ Node *n = *ptr;
+
+ if (!n) {
+ *ptr = n = (Node *) malloc(sizeof(Node));
+ n->addr = addr;
+ n->version = 0;
+ n->tries = 0;
+ n->u.interfaces = 0;
+ n->left = n->right = 0;
+ return n;
+ } else if (addr == n->addr)
+ return n;
+ else if (addr < n->addr)
+ return find_node(addr, &(n->left));
+ else
+ return find_node(addr, &(n->right));
+}
+
+
+Interface *find_interface(addr, node)
+ u_int32 addr;
+ Node *node;
+{
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+ if (ifc->addr == addr)
+ return ifc;
+
+ ifc = (Interface *) malloc(sizeof(Interface));
+ ifc->addr = addr;
+ ifc->next = node->u.interfaces;
+ node->u.interfaces = ifc;
+ ifc->neighbors = 0;
+
+ return ifc;
+}
+
+
+Neighbor *find_neighbor(addr, node)
+ u_int32 addr;
+ Node *node;
+{
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+
+ for (nb = ifc->neighbors; nb; nb = nb->next)
+ if (nb->addr == addr)
+ return nb;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level. For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap);
+#endif
+
+ switch (debug) {
+ case 0: if (severity > LOG_WARNING) return;
+ case 1: if (severity > LOG_NOTICE ) return;
+ case 2: if (severity > LOG_INFO ) return;
+ default:
+ fmt[0] = '\0';
+ if (severity == LOG_WARNING)
+ strcpy(fmt, "warning - ");
+ strncat(fmt, format, sizeof(fmt)-strlen(fmt));
+ fmt[sizeof(fmt)-1]='\0';
+ vfprintf(stderr, fmt, ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+
+ if (severity <= LOG_ERR)
+ exit(1);
+}
+
+
+/*
+ * Send a neighbors-list request.
+ */
+void ask(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+void ask2(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+
+/*
+ * Process an incoming group membership report.
+ */
+void accept_group_report(src, dst, group, r_type)
+ u_int32 src, dst, group;
+ int r_type;
+{
+ log(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void accept_probe(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+ log(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming route report message.
+ */
+void accept_report(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+ log(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list request message.
+ */
+void accept_neighbor_request(src, dst)
+ u_int32 src, dst;
+{
+ if (src != our_addr)
+ log(LOG_INFO, 0,
+ "ignoring spurious DVMRP neighbor request from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+void accept_neighbor_request2(src, dst)
+ u_int32 src, dst;
+{
+ if (src != our_addr)
+ log(LOG_INFO, 0,
+ "ignoring spurious DVMRP neighbor request2 from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void accept_neighbors(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ Node *node = find_node(src, &routers);
+
+ if (node->tries == 0) /* Never heard of 'em; must have hit them at */
+ node->tries = 1; /* least once, though...*/
+ else if (node->tries == -1) /* follow alias link */
+ node = node->u.alias;
+
+#define GET_ADDR(a) (a = ((u_int32)*p++ << 24), a += ((u_int32)*p++ << 16),\
+ a += ((u_int32)*p++ << 8), a += *p++)
+
+ /* if node is running a recent mrouted, ask for additional info */
+ if (level != 0) {
+ node->version = level;
+ node->tries = 1;
+ ask2(src);
+ return;
+ }
+
+ if (debug > 3) {
+ int i;
+
+ fprintf(stderr, " datalen = %d\n", datalen);
+ for (i = 0; i < datalen; i++) {
+ if ((i & 0xF) == 0)
+ fprintf(stderr, " ");
+ fprintf(stderr, " %02x", p[i]);
+ if ((i & 0xF) == 0xF)
+ fprintf(stderr, "\n");
+ }
+ if ((datalen & 0xF) != 0xF)
+ fprintf(stderr, "\n");
+ }
+
+ while (datalen > 0) { /* loop through interfaces */
+ u_int32 ifc_addr;
+ u_char metric, threshold, ncount;
+ Node *ifc_node;
+ Interface *ifc;
+ Neighbor *old_neighbors;
+
+ if (datalen < 4 + 3) {
+ log(LOG_WARNING, 0, "received truncated interface record from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ GET_ADDR(ifc_addr);
+ ifc_addr = htonl(ifc_addr);
+ metric = *p++;
+ threshold = *p++;
+ ncount = *p++;
+ datalen -= 4 + 3;
+
+ /* Fix up any alias information */
+ ifc_node = find_node(ifc_addr, &routers);
+ if (ifc_node->tries == 0) { /* new node */
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ } else if (ifc_node != node
+ && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
+ /* must merge two hosts' nodes */
+ Interface *ifc_i, *next_ifc_i;
+
+ if (ifc_node->tries == -1) {
+ Node *tmp = ifc_node->u.alias;
+
+ ifc_node->u.alias = node;
+ ifc_node = tmp;
+ }
+
+ /* Merge ifc_node (foo_i) into node (foo_n) */
+
+ if (ifc_node->tries > node->tries)
+ node->tries = ifc_node->tries;
+
+ for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+ Neighbor *nb_i, *next_nb_i, *nb_n;
+ Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+ old_neighbors = ifc_n->neighbors;
+ for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+ next_nb_i = nb_i->next;
+ for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+ if (nb_i->addr == nb_n->addr) {
+ if (nb_i->metric != nb_n->metric
+ || nb_i->threshold != nb_n->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb_i->addr, s1),
+ inet_fmt(node->addr, s2));
+ free(nb_i);
+ break;
+ }
+ if (!nb_n) { /* no match for this neighbor yet */
+ nb_i->next = ifc_n->neighbors;
+ ifc_n->neighbors = nb_i;
+ }
+ }
+
+ next_ifc_i = ifc_i->next;
+ free(ifc_i);
+ }
+
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ }
+
+ ifc = find_interface(ifc_addr, node);
+ old_neighbors = ifc->neighbors;
+
+ /* Add the neighbors for this interface */
+ while (ncount--) {
+ u_int32 neighbor;
+ Neighbor *nb;
+ Node *n_node;
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ GET_ADDR(neighbor);
+ neighbor = htonl(neighbor);
+ datalen -= 4;
+
+ for (nb = old_neighbors; nb; nb = nb->next)
+ if (nb->addr == neighbor) {
+ if (metric != nb->metric || threshold != nb->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+ goto next_neighbor;
+ }
+
+ nb = (Neighbor *) malloc(sizeof(Neighbor));
+ nb->next = ifc->neighbors;
+ ifc->neighbors = nb;
+ nb->addr = neighbor;
+ nb->metric = metric;
+ nb->threshold = threshold;
+ nb->flags = 0;
+
+ n_node = find_node(neighbor, &routers);
+ if (n_node->tries == 0 && !target_addr) { /* it's a new router */
+ ask(neighbor);
+ n_node->tries = 1;
+ }
+
+ next_neighbor: ;
+ }
+ }
+}
+
+void accept_neighbors2(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ Node *node = find_node(src, &routers);
+ u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
+ /* well, only possibly_broken_cisco, but that's too long to type. */
+
+ if (node->tries == 0) /* Never heard of 'em; must have hit them at */
+ node->tries = 1; /* least once, though...*/
+ else if (node->tries == -1) /* follow alias link */
+ node = node->u.alias;
+
+ while (datalen > 0) { /* loop through interfaces */
+ u_int32 ifc_addr;
+ u_char metric, threshold, ncount, flags;
+ Node *ifc_node;
+ Interface *ifc;
+ Neighbor *old_neighbors;
+
+ if (datalen < 4 + 4) {
+ log(LOG_WARNING, 0, "received truncated interface record from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ ifc_addr = *(u_int32*)p;
+ p += 4;
+ metric = *p++;
+ threshold = *p++;
+ flags = *p++;
+ ncount = *p++;
+ datalen -= 4 + 4;
+
+ if (broken_cisco && ncount == 0) /* dumb Ciscos */
+ ncount = 1;
+ if (broken_cisco && ncount > 15) /* dumb Ciscos */
+ ncount = ncount & 0xf;
+
+ /* Fix up any alias information */
+ ifc_node = find_node(ifc_addr, &routers);
+ if (ifc_node->tries == 0) { /* new node */
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ } else if (ifc_node != node
+ && (ifc_node->tries > 0 || ifc_node->u.alias != node)) {
+ /* must merge two hosts' nodes */
+ Interface *ifc_i, *next_ifc_i;
+
+ if (ifc_node->tries == -1) {
+ Node *tmp = ifc_node->u.alias;
+
+ ifc_node->u.alias = node;
+ ifc_node = tmp;
+ }
+
+ /* Merge ifc_node (foo_i) into node (foo_n) */
+
+ if (ifc_node->tries > node->tries)
+ node->tries = ifc_node->tries;
+
+ for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
+ Neighbor *nb_i, *next_nb_i, *nb_n;
+ Interface *ifc_n = find_interface(ifc_i->addr, node);
+
+ old_neighbors = ifc_n->neighbors;
+ for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
+ next_nb_i = nb_i->next;
+ for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
+ if (nb_i->addr == nb_n->addr) {
+ if (nb_i->metric != nb_n->metric
+ || nb_i->threshold != nb_i->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb_i->addr, s1),
+ inet_fmt(node->addr, s2));
+ free(nb_i);
+ break;
+ }
+ if (!nb_n) { /* no match for this neighbor yet */
+ nb_i->next = ifc_n->neighbors;
+ ifc_n->neighbors = nb_i;
+ }
+ }
+
+ next_ifc_i = ifc_i->next;
+ free(ifc_i);
+ }
+
+ ifc_node->tries = -1;
+ ifc_node->u.alias = node;
+ }
+
+ ifc = find_interface(ifc_addr, node);
+ old_neighbors = ifc->neighbors;
+
+ /* Add the neighbors for this interface */
+ while (ncount-- && datalen > 0) {
+ u_int32 neighbor;
+ Neighbor *nb;
+ Node *n_node;
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0, "received truncated neighbor list from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ neighbor = *(u_int32*)p;
+ p += 4;
+ datalen -= 4;
+ if (neighbor == 0)
+ /* make leaf nets point to themselves */
+ neighbor = ifc_addr;
+
+ for (nb = old_neighbors; nb; nb = nb->next)
+ if (nb->addr == neighbor) {
+ if (metric != nb->metric || threshold != nb->threshold)
+ log(LOG_WARNING, 0,
+ "inconsistent %s for neighbor %s of %s",
+ "metric/threshold",
+ inet_fmt(nb->addr, s1), inet_fmt(node->addr, s2));
+ goto next_neighbor;
+ }
+
+ nb = (Neighbor *) malloc(sizeof(Neighbor));
+ nb->next = ifc->neighbors;
+ ifc->neighbors = nb;
+ nb->addr = neighbor;
+ nb->metric = metric;
+ nb->threshold = threshold;
+ nb->flags = flags | NF_PRESENT;
+
+ n_node = find_node(neighbor, &routers);
+ if (n_node->tries == 0 && !target_addr) { /* it's a new router */
+ ask(neighbor);
+ n_node->tries = 1;
+ }
+
+ next_neighbor: ;
+ }
+ }
+}
+
+
+void check_vif_state()
+{
+ log(LOG_NOTICE, 0, "network marked down...");
+}
+
+
+int retry_requests(node)
+ Node *node;
+{
+ int result;
+
+ if (node) {
+ result = retry_requests(node->left);
+ if (node->tries > 0 && node->tries < retries) {
+ if (node->version)
+ ask2(node->addr);
+ else
+ ask(node->addr);
+ node->tries++;
+ result = 1;
+ }
+ return retry_requests(node->right) || result;
+ } else
+ return 0;
+}
+
+
+char *inet_name(addr)
+ u_int32 addr;
+{
+ struct hostent *e;
+
+ e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+ return e ? e->h_name : 0;
+}
+
+
+void print_map(node)
+ Node *node;
+{
+ if (node) {
+ char *name, *addr;
+
+ print_map(node->left);
+
+ addr = inet_fmt(node->addr, s1);
+ if (!target_addr
+ || (node->tries >= 0 && node->u.interfaces)
+ || (node->tries == -1
+ && node->u.alias->tries >= 0
+ && node->u.alias->u.interfaces)) {
+ if (show_names && (name = inet_name(node->addr)))
+ printf("%s (%s):", addr, name);
+ else
+ printf("%s:", addr);
+ if (node->tries < 0)
+ printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr, s1));
+ else if (!node->u.interfaces)
+ printf(" no response to query\n\n");
+ else {
+ Interface *ifc;
+
+ if (node->version)
+ printf(" <v%d.%d>", node->version & 0xff,
+ (node->version >> 8) & 0xff);
+ printf("\n");
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+ char *ifc_name = inet_fmt(ifc->addr, s1);
+ int ifc_len = strlen(ifc_name);
+ int count = 0;
+
+ printf(" %s:", ifc_name);
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ if (count > 0)
+ printf("%*s", ifc_len + 5, "");
+ printf(" %s", inet_fmt(nb->addr, s1));
+ if (show_names && (name = inet_name(nb->addr)))
+ printf(" (%s)", name);
+ printf(" [%d/%d", nb->metric, nb->threshold);
+ if (nb->flags) {
+ u_short flags = nb->flags;
+ if (flags & DVMRP_NF_TUNNEL)
+ printf("/tunnel");
+ if (flags & DVMRP_NF_SRCRT)
+ printf("/srcrt");
+ if (flags & DVMRP_NF_QUERIER)
+ printf("/querier");
+ if (flags & DVMRP_NF_DISABLED)
+ printf("/disabled");
+ if (flags & DVMRP_NF_DOWN)
+ printf("/down");
+ }
+ printf("]\n");
+ count++;
+ }
+ }
+ printf("\n");
+ }
+ }
+ print_map(node->right);
+ }
+}
+
+
+char *graph_name(addr, buf, len)
+ u_int32 addr;
+ char *buf;
+ int len;
+{
+ char *name;
+
+ if (len < sizeof("255.255.255.255")) {
+ fprintf(stderr,
+"Buffer too small in graph_name, provided %d bytes, but needed %d.\n",
+ len, sizeof("255.255.255.255"));
+ return NULL;
+ }
+ if (show_names && (name = inet_name(addr))) {
+ strncpy(buf, name, len - 1);
+ buf[len - 1] = '\0';
+ } else
+ inet_fmt(addr, buf);
+
+ return buf;
+}
+
+
+void graph_edges(node)
+ Node *node;
+{
+ Interface *ifc;
+ Neighbor *nb;
+ char name[100];
+
+ if (node) {
+ graph_edges(node->left);
+ if (node->tries >= 0) {
+ printf(" %d {$ NP %d0 %d0 $} \"%s%s\" \n",
+ (int) node->addr,
+ node->addr & 0xFF, (node->addr >> 8) & 0xFF,
+ graph_name(node->addr, name, sizeof(name)),
+ node->u.interfaces ? "" : "*");
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ Node *nb_node = find_node(nb->addr, &routers);
+ Neighbor *nb2;
+
+ if (nb_node->tries < 0)
+ nb_node = nb_node->u.alias;
+
+ if (node != nb_node &&
+ (!(nb2 = find_neighbor(node->addr, nb_node))
+ || node->addr < nb_node->addr)) {
+ printf(" %d \"%d/%d",
+ nb_node->addr, nb->metric, nb->threshold);
+ if (nb2 && (nb2->metric != nb->metric
+ || nb2->threshold != nb->threshold))
+ printf(",%d/%d", nb2->metric, nb2->threshold);
+ if (nb->flags & NF_PRESENT)
+ printf("%s%s",
+ nb->flags & DVMRP_NF_SRCRT ? "" :
+ nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
+ nb->flags & DVMRP_NF_DOWN ? "D" : "");
+ printf("\"\n");
+ }
+ }
+ printf(" ;\n");
+ }
+ graph_edges(node->right);
+ }
+}
+
+void elide_aliases(node)
+ Node *node;
+{
+ if (node) {
+ elide_aliases(node->left);
+ if (node->tries >= 0) {
+ Interface *ifc;
+
+ for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
+ Neighbor *nb;
+
+ for (nb = ifc->neighbors; nb; nb = nb->next) {
+ Node *nb_node = find_node(nb->addr, &routers);
+
+ if (nb_node->tries < 0)
+ nb->addr = nb_node->u.alias->addr;
+ }
+ }
+ }
+ elide_aliases(node->right);
+ }
+}
+
+void graph_map()
+{
+ time_t now = time(0);
+ char *nowstr = ctime(&now);
+
+ nowstr[24] = '\0'; /* Kill the newline at the end */
+ elide_aliases(routers);
+ printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
+ nowstr);
+ graph_edges(routers);
+ printf("END\n");
+}
+
+
+int get_number(var, deflt, pargv, pargc)
+ int *var, *pargc, deflt;
+ char ***pargv;
+{
+ if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
+ if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+ (*pargv)++, (*pargc)--;
+ *var = atoi((*pargv)[0]);
+ return 1;
+ } else if (deflt >= 0) {
+ *var = deflt;
+ return 1;
+ } else
+ return 0;
+ } else { /* Get value from the rest of this argument */
+ if (isdigit((*pargv)[0][2])) {
+ *var = atoi((*pargv)[0] + 2);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+
+u_int32 host_addr(name)
+ char *name;
+{
+ struct hostent *e = gethostbyname(name);
+ int addr;
+
+ if (e && e->h_length == sizeof(addr))
+ memcpy(&addr, e->h_addr_list[0], e->h_length);
+ else {
+ addr = inet_addr(name);
+ if (addr == -1)
+ addr = 0;
+ }
+
+ return addr;
+}
+
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int flood = FALSE, graph = FALSE;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ init_igmp();
+ setuid(getuid());
+
+ setlinebuf(stderr);
+
+ argv++, argc--;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'd':
+ if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+ usage();
+ break;
+ case 'f':
+ flood = TRUE;
+ break;
+ case 'g':
+ graph = TRUE;
+ break;
+ case 'n':
+ show_names = FALSE;
+ break;
+ case 'r':
+ if (!get_number(&retries, -1, &argv, &argc))
+ usage();
+ break;
+ case 't':
+ if (!get_number(&timeout, -1, &argv, &argc))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ argv++, argc--;
+ }
+
+ if (argc > 1) {
+ usage();
+ } else if (argc == 1 && !(target_addr = host_addr(argv[0])))
+ errx(2, "unknown host: %s", argv[0]);
+
+ if (debug)
+ fprintf(stderr, "Debug level %u\n", debug);
+
+ { /* Find a good local address for us. */
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+
+ addr.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ addr.sin_len = sizeof addr;
+#endif
+ addr.sin_addr.s_addr = dvmrp_group;
+ addr.sin_port = htons(2000); /* any port over 1024 will do... */
+ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+ || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
+ || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0)
+ err(-1, "determining local address");
+ close(udp);
+ our_addr = addr.sin_addr.s_addr;
+ }
+
+ /* Send initial seed message to all local routers */
+ ask(target_addr ? target_addr : allhosts_group);
+
+ if (target_addr) {
+ Node *n = find_node(target_addr, &routers);
+
+ n->tries = 1;
+
+ if (flood)
+ target_addr = 0;
+ }
+
+ /* Main receive loop */
+ for(;;) {
+ fd_set fds;
+ struct timeval tv;
+ int count, recvlen, dummy = 0;
+
+ if (igmp_socket >= FD_SETSIZE)
+ log(LOG_ERR, 0, "descriptor too big");
+ FD_ZERO(&fds);
+ FD_SET(igmp_socket, &fds);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ count = select(igmp_socket + 1, &fds, 0, 0, &tv);
+
+ if (count < 0) {
+ if (errno != EINTR)
+ warn("select");
+ continue;
+ } else if (count == 0) {
+ log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+ if (retry_requests(routers))
+ continue;
+ else
+ break;
+ }
+
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen >= 0)
+ accept_igmp(recvlen);
+ else if (errno != EINTR)
+ warn("recvfrom");
+ }
+
+ printf("\n");
+
+ if (graph)
+ graph_map();
+ else {
+ if (!target_addr)
+ printf("Multicast Router Connectivity:\n\n");
+ print_map(routers);
+ }
+
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: map-mbone [-f] [-g] [-n] [-t timeout] [-r retries]",
+ " [-d [debug-level]] [router]");
+ exit(1);
+}
+
+/* dummies */
+void accept_prune(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_graft(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_g_ack(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void add_table_entry(origin, mcastgrp)
+ u_int32 origin, mcastgrp;
+{
+}
+void accept_leave_message(src, dst, group)
+ u_int32 src, dst, group;
+{
+}
+void accept_mtrace(src, dst, group, data, no, datalen)
+ u_int32 src, dst, group;
+ char *data;
+ u_int no;
+ int datalen;
+{
+}
+void accept_membership_query(src, dst, group, tmo)
+ u_int32 src, dst, group;
+ int tmo;
+{
+}
+void accept_info_request(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+}
+void accept_info_reply(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+}
diff --git a/usr.sbin/mrouted/mrinfo.8 b/usr.sbin/mrouted/mrinfo.8
new file mode 100644
index 0000000..bd9c61f
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo.8
@@ -0,0 +1,91 @@
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MRINFO 8
+.Os
+.Sh NAME
+.Nm mrinfo
+.Nd display configuration info from a multicast router
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar debug_level
+.Op Fl r Ar retry_count
+.Op Fl t Ar timeout_count
+.Ar multicast_router
+.Sh DESCRIPTION
+The
+.Nm
+utility
+attempts to display the configuration information from the multicast router
+.Ar multicast_router .
+.Pp
+The
+.Nm
+utility uses the ASK_NEIGHBORS IGMP message to the specified multicast router.
+If this
+multicast router responds, the version number and a list of their neighboring
+multicast router addresses is part of that response.
+If the responding router
+has a recent multicast version number, then
+.Nm
+requests additional information such as metrics, thresholds, and flags from the
+multicast router.
+Once the specified multicast router responds, the
+configuration is displayed to the standard output.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar debug_level
+Set the debug level.
+When the debug level is greater than the
+default value of 0, addition debugging messages are printed.
+Regardless of
+the debug level, an error condition, will always write an error message and will
+cause
+.Nm
+to terminate.
+Non-zero debug levels have the following effects:
+.Bl -tag -width indent
+.It "level 1"
+packet warnings are printed to stderr.
+.It "level 2"
+all level 1 messages plus notifications down networks are printed to stderr.
+.It "level 3"
+all level 2 messages plus notifications of all packet
+timeouts are printed to stderr.
+.El
+.It Fl r Ar retry_count
+Set the neighbor query retry limit.
+Default is 3 retries.
+.It Fl t Ar timeout_count
+Set the number of seconds to wait for a neighbor query
+reply.
+Default timeout is 4 seconds.
+.El
+.Sh SAMPLE OUTPUT
+.Bd -literal
+.Nm mrinfo Ar mbone.phony.dom.net
+127.148.176.10 (mbone.phony.dom.net) [version 3.3]:
+ 127.148.176.10 -> 0.0.0.0 (?) [1/1/querier]
+ 127.148.176.10 -> 127.0.8.4 (mbone2.phony.dom.net) [1/45/tunnel]
+ 127.148.176.10 -> 105.1.41.9 (momoney.com) [1/32/tunnel/down]
+ 127.148.176.10 -> 143.192.152.119 (mbone.dipu.edu) [1/32/tunnel]
+.Ed
+.Pp
+For each neighbor of the queried multicast router, the IP of the queried router
+is displayed, followed by the IP and name of the neighbor.
+In square brackets
+the metric (cost of connection), the threshold (multicast ttl) is displayed.
+If
+the queried multicast router has a newer version number, the type (tunnel,
+srcrt) and status (disabled, down) of the connection is displayed.
+.Sh IMPORTANT NOTE
+The
+.Nm
+utility must be run as root.
+.Sh SEE ALSO
+.Xr map-mbone 8 ,
+.Xr mrouted 8 ,
+.Xr mtrace 8
+.Sh AUTHORS
+.An Van Jacobson
diff --git a/usr.sbin/mrouted/mrinfo.c b/usr.sbin/mrouted/mrinfo.c
new file mode 100644
index 0000000..1df6577
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo.c
@@ -0,0 +1,636 @@
+/*
+ * This tool requests configuration info from a multicast router
+ * and prints the reply (if any). Invoke it as:
+ *
+ * mrinfo router-name-or-address
+ *
+ * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
+ * multicast mapper written by Pavel Curtis).
+ *
+ * The lawyers insist we include the following UC copyright notice.
+ * The mapper from which this is derived contained a Xerox copyright
+ * notice which follows the UC one. Try not to get depressed noting
+ * that the legal gibberish is larger than the program.
+ *
+ * Copyright (c) 1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ---------------------------------
+ * Copyright (c) Xerox Corporation 1992. All rights reserved.
+ *
+ * License is granted to copy, to use, and to make and to use derivative works
+ * for research and evaluation purposes, provided that Xerox is acknowledged
+ * in all documentation pertaining to any such copy or derivative work. Xerox
+ * grants no other licenses expressed or implied. The Xerox trade name should
+ * not be used in any advertising without its written permission.
+ *
+ * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
+ * ANY PARTICULAR PURPOSE. The software is provided "as is" without express
+ * or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+/* original rcsid:
+ "@(#) Header: mrinfo.c,v 1.6 93/04/08 15:14:16 van Exp (LBL)";
+*/
+#endif
+
+#include <err.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include "defs.h"
+#include <arpa/inet.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#define DEFAULT_TIMEOUT 4 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3 /* How many times to ask each router */
+
+u_int32 our_addr, target_addr = 0; /* in NET order */
+int debug = 0;
+int nflag = 0;
+int retries = DEFAULT_RETRIES;
+int timeout = DEFAULT_TIMEOUT;
+int target_level = 0;
+vifi_t numvifs; /* to keep loader happy */
+ /* (see COPY_TABLES macro called in kern.c) */
+
+char * inet_name __P((u_int32 addr));
+void ask __P((u_int32 dst));
+void ask2 __P((u_int32 dst));
+int get_number __P((int *var, int deflt, char ***pargv,
+ int *pargc));
+u_int32 host_addr __P((char *name));
+static void usage __P((void));
+
+/* to shut up -Wstrict-prototypes */
+int main __P((int argc, char *argv[]));
+
+
+char *
+inet_name(addr)
+ u_int32 addr;
+{
+ struct hostent *e;
+ struct in_addr in;
+
+ if (addr == 0)
+ return "local";
+
+ if (nflag ||
+ (e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) {
+ in.s_addr = addr;
+ return (inet_ntoa(in));
+ }
+ return (e->h_name);
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity of the
+ * message and the current debug level. For errors of severity LOG_ERR or
+ * worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap, format);
+#else
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap);
+#endif
+ switch (debug) {
+ case 0:
+ if (severity > LOG_WARNING)
+ return;
+ case 1:
+ if (severity > LOG_NOTICE)
+ return;
+ case 2:
+ if (severity > LOG_INFO)
+ return;
+ default:
+ fmt[0] = '\0';
+ if (severity == LOG_WARNING)
+ strcpy(fmt, "warning - ");
+ strncat(fmt, format, sizeof(fmt)-strlen(fmt));
+ fmt[sizeof(fmt)-1]='\0';
+ vfprintf(stderr, fmt, ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+
+ if (severity <= LOG_ERR)
+ exit(1);
+}
+
+/*
+ * Send a neighbors-list request.
+ */
+void
+ask(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+void
+ask2(dst)
+ u_int32 dst;
+{
+ send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), 0);
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void
+accept_neighbors(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ u_char *ep = p + datalen;
+#define GET_ADDR(a) (a = ((u_int32)*p++ << 24), a += ((u_int32)*p++ << 16),\
+ a += ((u_int32)*p++ << 8), a += *p++)
+
+ printf("%s (%s):\n", inet_fmt(src, s1), inet_name(src));
+ while (p < ep) {
+ register u_int32 laddr;
+ register u_char metric;
+ register u_char thresh;
+ register int ncount;
+
+ GET_ADDR(laddr);
+ laddr = htonl(laddr);
+ metric = *p++;
+ thresh = *p++;
+ ncount = *p++;
+ while (--ncount >= 0) {
+ register u_int32 neighbor;
+ GET_ADDR(neighbor);
+ neighbor = htonl(neighbor);
+ printf(" %s -> ", inet_fmt(laddr, s1));
+ printf("%s (%s) [%d/%d]\n", inet_fmt(neighbor, s1),
+ inet_name(neighbor), metric, thresh);
+ }
+ }
+}
+
+void
+accept_neighbors2(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ u_char *ep = p + datalen;
+ u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
+ /* well, only possibly_broken_cisco, but that's too long to type. */
+ u_int majvers = level & 0xff;
+ u_int minvers = (level >> 8) & 0xff;
+
+ printf("%s (%s) [", inet_fmt(src, s1), inet_name(src));
+ if (majvers == 3 && minvers == 0xff)
+ printf("DVMRPv3 compliant");
+ else
+ printf("version %d.%d", majvers, minvers);
+ printf ("]:\n");
+
+ while (p < ep) {
+ register u_char metric;
+ register u_char thresh;
+ register u_char flags;
+ register int ncount;
+ register u_int32 laddr = *(u_int32*)p;
+
+ p += 4;
+ metric = *p++;
+ thresh = *p++;
+ flags = *p++;
+ ncount = *p++;
+ if (broken_cisco && ncount == 0) /* dumb Ciscos */
+ ncount = 1;
+ if (broken_cisco && ncount > 15) /* dumb Ciscos */
+ ncount = ncount & 0xf;
+ while (--ncount >= 0 && p < ep) {
+ register u_int32 neighbor = *(u_int32*)p;
+ p += 4;
+ printf(" %s -> ", inet_fmt(laddr, s1));
+ printf("%s (%s) [%d/%d", inet_fmt(neighbor, s1),
+ inet_name(neighbor), metric, thresh);
+ if (flags & DVMRP_NF_TUNNEL)
+ printf("/tunnel");
+ if (flags & DVMRP_NF_SRCRT)
+ printf("/srcrt");
+ if (flags & DVMRP_NF_PIM)
+ printf("/pim");
+ if (flags & DVMRP_NF_QUERIER)
+ printf("/querier");
+ if (flags & DVMRP_NF_DISABLED)
+ printf("/disabled");
+ if (flags & DVMRP_NF_DOWN)
+ printf("/down");
+ if (flags & DVMRP_NF_LEAF)
+ printf("/leaf");
+ printf("]\n");
+ }
+ }
+}
+
+int
+get_number(var, deflt, pargv, pargc)
+ int *var, *pargc, deflt;
+ char ***pargv;
+{
+ if ((*pargv)[0][2] == '\0') { /* Get the value from the next
+ * argument */
+ if (*pargc > 1 && isdigit((*pargv)[1][0])) {
+ (*pargv)++, (*pargc)--;
+ *var = atoi((*pargv)[0]);
+ return 1;
+ } else if (deflt >= 0) {
+ *var = deflt;
+ return 1;
+ } else
+ return 0;
+ } else { /* Get value from the rest of this argument */
+ if (isdigit((*pargv)[0][2])) {
+ *var = atoi((*pargv)[0] + 2);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mrinfo [-n] [-t timeout] [-r retries] [router]\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int tries;
+ int trynew;
+ struct timeval et;
+ struct hostent *hp;
+ struct hostent bogus;
+ char *host;
+ int curaddr;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ init_igmp();
+ setuid(getuid());
+
+ setlinebuf(stderr);
+
+ argv++, argc--;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'd':
+ if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
+ usage();
+ break;
+ case 'n':
+ ++nflag;
+ break;
+ case 'r':
+ if (!get_number(&retries, -1, &argv, &argc))
+ usage();
+ break;
+ case 't':
+ if (!get_number(&timeout, -1, &argv, &argc))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ argv++, argc--;
+ }
+ if (argc > 1)
+ usage();
+ if (argc == 1)
+ host = argv[0];
+ else
+ host = "127.0.0.1";
+
+ if ((target_addr = inet_addr(host)) != -1) {
+ hp = &bogus;
+ hp->h_length = sizeof(target_addr);
+ hp->h_addr_list = (char **)malloc(2 * sizeof(char *));
+ hp->h_addr_list[0] = malloc(hp->h_length);
+ memcpy(hp->h_addr_list[0], &target_addr, hp->h_length);
+ hp->h_addr_list[1] = 0;
+ } else
+ hp = gethostbyname(host);
+
+ if (hp == NULL || hp->h_length != sizeof(target_addr))
+ errx(1, "%s: no such host", argv[0]);
+ if (debug)
+ fprintf(stderr, "Debug level %u\n", debug);
+
+ /* Check all addresses; mrouters often have unreachable interfaces */
+ for (curaddr = 0; hp->h_addr_list[curaddr] != NULL; curaddr++) {
+ memcpy(&target_addr, hp->h_addr_list[curaddr], hp->h_length);
+ { /* Find a good local address for us. */
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+
+ addr.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ addr.sin_len = sizeof addr;
+#endif
+ addr.sin_addr.s_addr = target_addr;
+ addr.sin_port = htons(2000); /* any port over 1024 will
+ * do... */
+ if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+ || connect(udp, (struct sockaddr *) & addr, sizeof(addr)) < 0
+ || getsockname(udp, (struct sockaddr *) & addr, &addrlen) < 0)
+ err(-1, "determining local address");
+ close(udp);
+ our_addr = addr.sin_addr.s_addr;
+ }
+
+ tries = 0;
+ trynew = 1;
+ /*
+ * New strategy: send 'ask2' for two timeouts, then fall back
+ * to 'ask', since it's not very likely that we are going to
+ * find someone who only responds to 'ask' these days
+ */
+ ask2(target_addr);
+
+ gettimeofday(&et, 0);
+ et.tv_sec += timeout;
+
+ /* Main receive loop */
+ for (;;) {
+ fd_set fds;
+ struct timeval tv, now;
+ int count, recvlen, dummy = 0;
+ register u_int32 src, dst, group;
+ struct ip *ip;
+ struct igmp *igmp;
+ int ipdatalen, iphdrlen, igmpdatalen;
+
+ if (igmp_socket >= FD_SETSIZE)
+ log(LOG_ERR, 0, "descriptor too big");
+ FD_ZERO(&fds);
+ FD_SET(igmp_socket, &fds);
+
+ gettimeofday(&now, 0);
+ tv.tv_sec = et.tv_sec - now.tv_sec;
+ tv.tv_usec = et.tv_usec - now.tv_usec;
+
+ if (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000L;
+ --tv.tv_sec;
+ }
+ if (tv.tv_sec < 0)
+ tv.tv_sec = tv.tv_usec = 0;
+
+ count = select(igmp_socket + 1, &fds, 0, 0, &tv);
+
+ if (count < 0) {
+ if (errno != EINTR)
+ warn("select");
+ continue;
+ } else if (count == 0) {
+ log(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
+ if (++tries > retries)
+ break;
+ /* If we've tried ASK_NEIGHBORS2 twice with
+ * no response, fall back to ASK_NEIGHBORS
+ */
+ if (tries == 2 && target_level == 0)
+ trynew = 0;
+ if (target_level == 0 && trynew == 0)
+ ask(target_addr);
+ else
+ ask2(target_addr);
+ gettimeofday(&et, 0);
+ et.tv_sec += timeout;
+ continue;
+ }
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR)
+ warn("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ log(LOG_WARNING, 0,
+ "packet too short (%u bytes) for IP header",
+ recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0)
+ continue; /* Request to install cache entry */
+ src = ip->ip_src.s_addr;
+ dst = ip->ip_dst.s_addr;
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len) - iphdrlen;
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ log(LOG_WARNING, 0,
+ "packet shorter (%u bytes) than hdr+data length (%u+%u)",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ group = igmp->igmp_group.s_addr;
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ log(LOG_WARNING, 0,
+ "IP data field too short (%u bytes) for IGMP, from %s",
+ ipdatalen, inet_fmt(src, s1));
+ continue;
+ }
+ if (igmp->igmp_type != IGMP_DVMRP)
+ continue;
+
+ switch (igmp->igmp_code) {
+ case DVMRP_NEIGHBORS:
+ case DVMRP_NEIGHBORS2:
+ if (src != target_addr) {
+ warnx("got reply from %s instead of %s",
+ inet_fmt(src, s1), inet_fmt(target_addr, s1));
+ /*continue;*/
+ }
+ break;
+ default:
+ continue; /* ignore all other DVMRP messages */
+ }
+
+ switch (igmp->igmp_code) {
+
+ case DVMRP_NEIGHBORS:
+ if (group) {
+ /* knows about DVMRP_NEIGHBORS2 msg */
+ if (target_level == 0) {
+ target_level = ntohl(group);
+ ask2(target_addr);
+ }
+ } else {
+ accept_neighbors(src, dst, (u_char *)(igmp + 1),
+ igmpdatalen, ntohl(group));
+ exit(0);
+ }
+ break;
+
+ case DVMRP_NEIGHBORS2:
+ accept_neighbors2(src, dst, (u_char *)(igmp + 1),
+ igmpdatalen, ntohl(group));
+ exit(0);
+ }
+ }
+ }
+ exit(1);
+}
+
+/* dummies */
+void accept_probe(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+}
+void accept_group_report(src, dst, group, r_type)
+ u_int32 src, dst, group;
+ int r_type;
+{
+}
+void accept_neighbor_request2(src, dst)
+ u_int32 src, dst;
+{
+}
+void accept_report(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ char *p;
+ int datalen;
+{
+}
+void accept_neighbor_request(src, dst)
+ u_int32 src, dst;
+{
+}
+void accept_prune(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_graft(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void accept_g_ack(src, dst, p, datalen)
+ u_int32 src, dst;
+ char *p;
+ int datalen;
+{
+}
+void add_table_entry(origin, mcastgrp)
+ u_int32 origin, mcastgrp;
+{
+}
+void check_vif_state()
+{
+}
+void accept_leave_message(src, dst, group)
+ u_int32 src, dst, group;
+{
+}
+void accept_mtrace(src, dst, group, data, no, datalen)
+ u_int32 src, dst, group;
+ char *data;
+ u_int no;
+ int datalen;
+{
+}
+void accept_membership_query(src, dst, group, tmo)
+ u_int32 src, dst, group;
+ int tmo;
+{
+}
+void accept_info_request(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+}
+void accept_info_reply(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+}
diff --git a/usr.sbin/mrouted/mrinfo/Makefile b/usr.sbin/mrouted/mrinfo/Makefile
new file mode 100644
index 0000000..f111aa9
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= mrinfo
+MAN= mrinfo.8
+BINOWN= root
+BINMODE= 4555
+
+CFLAGS+= -I$S
+
+DPADD= ${LIBMROUTED}
+LDADD= ${LIBMROUTED}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8
new file mode 100644
index 0000000..ec699ac
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.8
@@ -0,0 +1,608 @@
+.\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MROUTED 8
+.Os
+.Sh NAME
+.Nm mrouted
+.Nd IP multicast routing daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar config_file
+.Op Fl d Op Ar debug_level
+.Op Fl p
+.Sh DESCRIPTION
+The
+.Nm
+utility is an implementation of the Distance-Vector Multicast Routing
+Protocol (DVMRP), an earlier version of which is specified in RFC-1075.
+It maintains topological knowledge via a distance-vector routing protocol
+(like RIP, described in RFC-1058), upon which it implements a multicast
+datagram forwarding algorithm called Reverse Path Multicasting.
+.Pp
+The
+.Nm
+utility forwards a multicast datagram along a shortest (reverse) path tree
+rooted at the subnet on which the datagram originates.
+The multicast
+delivery tree may be thought of as a broadcast delivery tree that has
+been pruned back so that it does not extend beyond those subnetworks
+that have members of the destination group.
+Hence, datagrams
+are not forwarded along those branches which have no listeners of the
+multicast group.
+The IP time-to-live of a multicast datagram can be
+used to limit the range of multicast datagrams.
+.Pp
+In order to support multicasting among subnets that are separated by (unicast)
+routers that do not support IP multicasting,
+.Nm
+includes support for
+"tunnels", which are virtual point-to-point links between pairs of
+multicast routers
+located anywhere in an internet. IP multicast packets are encapsulated for
+transmission through tunnels, so that they look like normal unicast datagrams
+to intervening routers and subnets. The encapsulation
+is added on entry to a tunnel, and stripped off
+on exit from a tunnel.
+The packets are encapsulated using the IP-in-IP protocol
+(IP protocol number 4).
+Older versions of
+.Nm
+tunneled using IP source routing, which puts a heavy load on some
+types of routers.
+This version does not support IP source route tunnelling.
+.Pp
+The tunnelling mechanism allows
+.Nm
+to establish a virtual internet, for
+the purpose of multicasting only, which is independent of the physical
+internet, and which may span multiple Autonomous Systems. This capability
+is intended for experimental support of internet multicasting only, pending
+widespread support for multicast routing by the regular (unicast) routers.
+The
+.Nm
+utility suffers from the well-known scaling problems of any distance-vector
+routing protocol, and does not (yet) support hierarchical multicast routing.
+.Pp
+The
+.Nm
+utility
+handles multicast routing only; there may or may not be unicast routing
+software running on the same machine as
+.Nm .
+With the use of tunnels, it
+is not necessary for
+.Nm
+to have access to more than one physical subnet
+in order to perform multicast forwarding.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar config_file
+Specify an alternative file for configuration commands.
+Default is
+.Pa /etc/mrouted.conf .
+.It Fl d Op Ar debug_level
+If no
+.Fl d
+option is given, or if the debug level is specified as 0,
+.Nm
+detaches from the invoking terminal. Otherwise, it remains attached to the
+invoking terminal and responsive to signals from that terminal.
+Regardless of the debug level,
+.Nm
+always writes warning and error messages to the system
+log daemon. The
+.Fl debug-level
+argument is a comma-separated list of any of the following:
+.Bl -tag -width indent
+.It "packet"
+Display the type, source and destination of all packets sent or received.
+.It "pruning"
+Display more information about prunes sent or received.
+.It "routing"
+Display more information about routing update packets sent or received.
+.It "route_detail"
+Display routing updates in excruciating detail. This is generally way too
+much information.
+.It "neighbors"
+Display information about neighbor discovery.
+.It "cache"
+Display insertions, deletions and refreshes of entries in
+the kernel forwarding cache.
+.It "timeout"
+Debug timeouts and periodic processes.
+.It "interface"
+Display information about interfaces and their configuration.
+.It "membership"
+Display information about group memberships on physical interfaces.
+.It "traceroute"
+Display information about multicast traceroute requests
+passing through this router.
+.It "igmp"
+Display IGMP operation including group membership and querier election.
+.It "icmp"
+Monitor ICMP handling.
+.It "rsrr"
+Monitor RSRR operation.
+.El
+.Pp
+Upon startup,
+.Nm
+writes its pid to the file
+.Pa /var/run/mrouted.pid .
+.El
+.Sh CONFIGURATION
+The
+.Nm
+utility automatically configures itself to forward on all multicast-capable
+interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding
+the loopback "interface"), and it finds other DVMRP routers directly reachable
+via those interfaces. To override the default configuration, or to add
+tunnel links to other multicast routers,
+configuration commands may be placed in
+.Pa /etc/mrouted.conf
+(or an alternative file, specified by the
+.Fl c
+option).
+.Pp
+The file format is free-form; whitespace (including newlines) is not
+significant.
+The file begins with commands that apply to
+.Nm Ns 's
+overall operation or set defaults.
+.Bl -tag -width indent
+.It cache_lifetime Ar secs
+Specifies, in seconds, the lifetime of a multicast forwarding cache
+entry in the kernel. Multicast forwarding cache entries in the kernel
+are checked every
+.Ar secs
+seconds, and are refreshed if the source is still
+active or deleted if not. Care should be taken when setting this value,
+as a low value can keep the kernel cache small at the cost of "thrashing"
+the cache for periodic senders, but high values can cause the kernel
+cache to grow unacceptably large. The default is 300 seconds (5 minutes).
+.It prune_lifetime Ar secs
+Specifies, in seconds, the average lifetime of prunes that are sent towards
+parents. The actual lifetimes will be randomized in the range
+[.5\fIsecs\fP,1.5\fIsecs\fP]. The default is 7200 (2 hours). Smaller values
+cause less state to be kept both at this router and the parent, at the
+cost of more frequent broadcasts. However, some routers (e.g.\&
+.Nm
+<3.3
+and all currently known versions of cisco's IOS) do not use the
+DVMRP generation ID to determine that a neighbor has rebooted. Prunes
+sent towards these neighbors should be kept short, in order to shorten
+the time to recover from a reboot. For use in this situation, the
+prune_lifetime keyword may be specified on an interface as described
+below.
+.It noflood
+The
+.Nm
+utility
+uses a DVMRP optimization to prevent having to keep individual routing tables
+for each neighbor; part of this optimization is that
+.Nm
+assumes that it is the forwarder for each of its attached subnets on
+startup. This can cause duplicates for a short period (approximately
+one full route report interval), since both the router that just
+started up and the proper forwarder will be forwarding traffic. This
+behavior can be turned off with the noflood keyword;
+.Nm
+will not assume that it is the forwarder on startup.
+Turning on noflood can cause black holes on restart, which will generally
+last approximately one full route report interval.
+The noflood keyword can also be specified on individual interfaces.
+.It rexmit_prunes Ar [on|off]
+Default is to retransmit prunes on all point-to-point interfaces
+(including tunnels) but no multi-access interfaces. This option
+may be used to make the default on (or off) for all interfaces.
+The rexmit_prunes keyword can also be specified on individual interfaces.
+.It name Ar "boundary-name scoped-addr/mask-len"
+Associates
+.Ar boundary-name
+with the boundary described by
+.Ar scoped-addr/mask-len ,
+to help make interface configurations
+more readable and reduce repetition in the configuration file.
+.El
+.Pp
+The second section of the configuration file, which may optionally
+be empty, describes options that apply to physical interfaces.
+.Bl -tag -width indent
+.It phyint Ar "local-addr|ifname"
+The phyint command does nothing by itself; it is simply a place holder
+which interface-specific commands may follow. An interface address or
+name may be specified.
+.It disable
+Disables multicast forwarding on this interface. By default,
+.Nm
+discovers all locally attached multicast capable interfaces and forwards
+on all of them.
+.It netmask Ar netmask
+If the kernel's netmask does not accurately reflect
+the subnet (e.g. you're using proxy-ARP in lieu of IP subnetting), use the
+netmask command to describe the real netmask.
+.It altnet Ar network/mask-len
+If a phyint is attached to multiple IP subnets, describe each additional subnet
+with the altnet keyword. This command may be specified multiple times
+to describe multiple subnets.
+.It igmpv1
+If there are any IGMPv1 routers on the phyint, use the \fBigmpv1\fP
+keyword to force
+.Nm
+into IGMPv1 mode. All routers on the phyint
+must use the same version of IGMP.
+.It force_leaf
+Force
+.Nm
+to ignore other routers on this interface.
+.Nm
+will never send or accept neighbor probes or
+route reports on this interface.
+.El
+.Pp
+In addition, the common vif commands described later may all be used on
+a phyint.
+.Pp
+The third section of the configuration file, also optional, describes
+the configuration of any DVMRP tunnels this router might have.
+.Bl -tag -width indent
+.It tunnel Ar "local-addr|ifname" Ar "remote-addr|remote-hostname"
+This command establishes a DVMRP tunnel between this host (on the interface
+described by
+.Ar local-addr
+or
+.Ar ifname )
+and a remote host (identified by
+.Ar remote-addr
+or
+.Ar remote-hostname ) .
+A remote hostname may only be used if
+it maps to a single IP address.
+A tunnel must be configured on both routers before it can be used.
+.Pp
+Be careful that the unicast route to the remote address goes out the
+interface specified by the
+.Ar "local-addr|ifname"
+argument. Some UNIX
+kernels rewrite the source address of
+.Nm Ns 's
+packets on their way out to contain the address of the transmission
+interface. This is best assured via a static host route.
+.El
+.Pp
+The common vif commands described below
+may all be used on tunnels or phyints.
+.Bl -tag -width indent
+.It metric Ar m
+The metric is the "cost" associated with receiving a datagram on the given
+interface or tunnel; it may be used to influence the choice of routes.
+The metric defaults to 1. Metrics should be kept as small as possible,
+because DVMRP cannot route along paths with a sum of metrics greater
+than 31.
+.It advert_metric Ar m
+The advert_metric is the "cost" associated with sending a datagram
+on the given interface or tunnel; it may be used to influence the choice
+of routes. The advert_metric defaults to 0. Note that the effective
+metric of a link is one end's metric plus the other end's advert_metric.
+.It threshold Ar t
+The threshold is the minimum IP time-to-live required for a multicast datagram
+to be forwarded to the given interface or tunnel. It is used to control the
+scope of multicast datagrams. (The TTL of forwarded packets is only compared
+to the threshold, it is not decremented by the threshold. Every multicast
+router decrements the TTL by exactly 1.) The default threshold is 1.
+.Pp
+In general, all multicast routers
+connected to a particular subnet or tunnel should
+use the same metric and threshold for that subnet or tunnel.
+.It rate_limit Ar r
+The rate_limit option allows the network administrator to specify a
+certain bandwidth in Kbits/second which would be allocated to multicast
+traffic. It defaults 0 (unlimited).
+.It boundary Ar "boundary-name|scoped-addr/mask-len"
+The boundary option allows an interface
+to be configured as an administrative boundary for the specified
+scoped address.
+Packets belonging to this address will not
+be forwarded on a scoped interface. The boundary option accepts either
+a name or a boundary spec. This command may be specified several times
+on an interface in order to describe multiple boundaries.
+.It passive
+No packets will be sent on this link or tunnel until we hear from the other
+end. This is useful for the "server" end of a tunnel that goes over
+a dial-on-demand link; configure the "server" end as passive and
+it will not send its periodic probes until it hears one from the other
+side, so will not keep the link up. If this option is specified on both
+ends of a tunnel, the tunnel will never come up.
+.It noflood
+As described above, but only applicable to this interface/tunnel.
+.It prune_lifetime Ar secs
+As described above, but only applicable to this interface/tunnel.
+.It rexmit_prunes Ar "[on|off]"
+As described above, but only applicable to this interface/tunnel.
+Recall that prune retransmission
+defaults to on for point-to-point links and tunnels, and to off for
+multi-access links.
+.It allow_nonpruners
+By default,
+.Nm
+refuses to peer with DVMRP neighbors that
+do not claim to support pruning. This option allows such peerings
+on this interface.
+.It notransit
+A specialized case of route filtering; no route learned from an interface
+marked "notransit" will be advertised on another interface marked
+"notransit". Marking only a single interface "notransit" has no meaning.
+.It accept|deny Ar "(route/mask-len [exact])+" Op bidir
+The
+.Li accept
+and
+.Li deny
+commands allow rudimentary route filtering. The
+.Li accept
+command causes
+.Nm
+to accept only the listed routes on the configured interface; the
+.Li deny
+command causes
+.Nm
+to accept all but the listed routes.
+Only one of
+.Li accept
+or
+.Li deny
+commands may be used on a given interface.
+.Pp
+The list of routes follows the
+.Li accept
+or
+.Li deny
+keyword. If the keyword
+.Ar exact
+follows a route, then only that route is matched; otherwise, that route
+and any more specific route is matched. For example,
+.Li deny 0/0
+denys all routes, while
+.Li deny 0/0 exact
+denys only the default route. The default route may also be specified
+with the
+.Li default
+keyword.
+.Pp
+The
+.Ar bidir
+keyword enables bidirectional route filtering; the filter will be applied
+to routes on both output and input. Without the
+.Ar bidir
+keyword,
+.Li accept
+and
+.Li deny
+filters are only applied on input. Poison reverse routes are never
+filtered out.
+.El
+.Pp
+The
+.Nm
+utility will not initiate execution if it has fewer than two enabled vifs,
+where a vif (virtual interface) is either a physical multicast-capable
+interface or a tunnel. It will log a warning if all of its vifs are
+tunnels; such an
+.Nm
+configuration would be better replaced by more
+direct tunnels (i.e. eliminate the middle man).
+.Sh "EXAMPLE CONFIGURATION"
+This is an example configuration for a mythical multicast router at a big
+school.
+.Pp
+.Bd -literal
+#
+# mrouted.conf example
+#
+# Name our boundaries to make it easier
+name LOCAL 239.255.0.0/16
+name EE 239.254.0.0/16
+#
+# le1 is our gateway to compsci, don't forward our
+# local groups to them
+phyint le1 boundary EE
+#
+# le2 is our interface on the classroom net, it has four
+# different length subnets on it.
+# note that you can use either an ip address or an
+# interface name
+phyint 172.16.12.38 boundary EE altnet 172.16.15.0/26
+ altnet 172.16.15.128/26 altnet 172.16.48.0/24
+#
+# atm0 is our ATM interface, which doesn't properly
+# support multicasting.
+phyint atm0 disable
+#
+# This is an internal tunnel to another EE subnet
+# Remove the default tunnel rate limit, since this
+# tunnel is over ethernets
+tunnel 192.168.5.4 192.168.55.101 metric 1 threshold 1
+ rate_limit 0
+#
+# This is our tunnel to the outside world.
+# Careful with those boundaries, Eugene.
+tunnel 192.168.5.4 10.11.12.13 metric 1 threshold 32
+ boundary LOCAL boundary EE
+.Ed
+.Sh SIGNALS
+The
+.Nm
+utility responds to the following signals:
+.Bl -tag -width indent
+.It HUP
+Restarts
+.Nm .
+The configuration file is reread every time this signal is evoked.
+.It INT
+Terminate execution gracefully (i.e., by sending
+good-bye messages to all neighboring routers).
+.It TERM
+Same as INT.
+.It USR1
+Dump the internal routing tables to
+.Pa /var/tmp/mrouted.dump .
+.It USR2
+Dump the internal cache tables to
+.Pa /var/tmp/mrouted.cache .
+.It QUIT
+Dump the internal routing tables to stderr (only if
+.Nm
+was invoked with a non-zero debug level).
+.El
+.Pp
+For convenience in sending signals,
+.Nm
+writes its pid to
+.Pa /var/run/mrouted.pid
+upon startup.
+.Sh EXAMPLES
+The routing tables look like this:
+.Pp
+.Bd -literal
+Virtual Interface Table
+ Vif Local-Address Metric Thresh Flags
+ 0 36.2.0.8 subnet: 36.2/16 1 1 querier
+ groups: 224.0.2.1
+ 224.0.0.4
+ pkts in: 3456
+ pkts out: 2322323
+
+ 1 36.11.0.1 subnet: 36.11/16 1 1 querier
+ groups: 224.0.2.1
+ 224.0.1.0
+ 224.0.0.4
+ pkts in: 345
+ pkts out: 3456
+
+ 2 36.2.0.8 tunnel: 36.8.0.77 3 1
+ peers: 36.8.0.77 (3.255)
+ boundaries: 239.0.1/24
+ : 239.1.2/24
+ pkts in: 34545433
+ pkts out: 234342
+
+ 3 36.2.0.8 tunnel: 36.6.8.23 3 16
+
+Multicast Routing Table (1136 entries)
+ Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs
+ 36.2 1 45 0 1* 2 3*
+ 36.8 36.8.0.77 4 15 2 0* 1* 3*
+ 36.11 1 20 1 0* 2 3*
+ .
+ .
+ .
+.Ed
+.Pp
+In this example, there are four vifs connecting to two subnets and two
+tunnels. The vif 3 tunnel is not in use (no peer address). The vif 0 and
+vif 1 subnets have some groups present; tunnels never have any groups. This
+instance of
+.Nm
+is the one responsible for sending periodic group
+membership queries on the vif 0 and vif 1 subnets, as indicated by the
+"querier" flags.
+The list of boundaries indicate the scoped addresses on that
+interface.
+A count of the no. of incoming and outgoing packets is also
+shown at each interface.
+.Pp
+Associated with each subnet from which a multicast datagram can originate
+is the address of the previous hop router (unless the subnet is directly-
+connected), the metric of the path back to the origin, the amount of time
+since we last received an update for this subnet, the incoming vif for
+multicasts from that origin, and a list of outgoing vifs. "*" means that
+the outgoing vif is connected to a leaf of the broadcast tree rooted at the
+origin, and a multicast datagram from that origin will be forwarded on that
+outgoing vif only if there are members of the destination group on that leaf.
+.Pp
+The
+.Nm
+utility also maintains a copy of the kernel forwarding cache table.
+Entries
+are created and deleted by
+.Nm .
+.Pp
+The cache tables look like this:
+.Pp
+.Bd -literal
+Multicast Routing Cache Table (147 entries)
+ Origin Mcast-group CTmr Age Ptmr IVif Forwvifs
+ 13.2.116/22 224.2.127.255 3m 2m - 0 1
+>13.2.116.19
+>13.2.116.196
+ 138.96.48/21 224.2.127.255 5m 2m - 0 1
+>138.96.48.108
+ 128.9.160/20 224.2.127.255 3m 2m - 0 1
+>128.9.160.45
+ 198.106.194/24 224.2.135.190 9m 28s 9m 0P
+>198.106.194.22
+.Ed
+.Pp
+Each entry is characterized by the origin subnet number and mask and the
+destination multicast group.
+.Pp
+The 'CTmr' field indicates the lifetime
+of the entry. The entry is deleted from the cache table
+(or refreshed, if traffic is flowing)
+when the timer decrements to zero. The 'Age' field is the time since
+this cache entry was originally created. Since cache entries get refreshed
+if traffic is flowing, routing entries can grow very old.
+.Pp
+The 'Ptmr' field is simply a dash if no prune was sent upstream, or the
+amount of time until the upstream prune will time out.
+.Pp
+The 'Ivif' field indicates the
+incoming vif for multicast packets from that origin. Each router also
+maintains a record of the number of prunes received from neighboring
+routers for a particular source and group.
+If there are no members of
+a multicast group on any downward link of the multicast tree for a
+subnet, a prune message is sent to the upstream router.
+They are
+indicated by a "P" after the vif number.
+.Pp
+The Forwvifs field shows the
+interfaces along which datagrams belonging to the source-group are
+forwarded.
+A "p" indicates that no datagrams are being forwarded along
+that interface.
+An unlisted interface is a leaf subnet with no
+members of the particular group on that subnet.
+A "b" on an interface
+indicates that it is a boundary interface, i.e. traffic will not be
+forwarded on the scoped address on that interface.
+.Pp
+An additional line with a ">" as the first character is printed for
+each source on the subnet. Note that there can be many sources in
+one subnet.
+An additional line with a "<" as the first character is printed
+describing any prunes received from downstream dependent neighbors
+for this subnet and group.
+.Sh FILES
+.Bl -tag -width /var/tmp/mrouted.cache -compact
+.It Pa /etc/mrouted.conf
+.It Pa /var/run/mrouted.pid
+.It Pa /var/tmp/mrouted.dump
+.It Pa /var/tmp/mrouted.cache
+.El
+.Sh SEE ALSO
+.Xr map-mbone 8 ,
+.Xr mrinfo 8 ,
+.Xr mtrace 8
+.Pp
+DVMRP is described, along with other multicast routing algorithms, in the
+paper "Multicast Routing in Internetworks and Extended LANs" by S. Deering,
+in the Proceedings of the ACM SIGCOMM '88 Conference.
+.Sh AUTHORS
+.An Steve Deering ,
+.An Ajit Thyagarajan ,
+.An Bill Fenner .
diff --git a/usr.sbin/mrouted/mrouted.conf b/usr.sbin/mrouted/mrouted.conf
new file mode 100644
index 0000000..e9cb04c
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.conf
@@ -0,0 +1,44 @@
+# $FreeBSD$
+# mrouted.conf,v 3.8 1995/11/29 22:40:47 fenner Rel
+#
+# This is the configuration file for "mrouted", an IP multicast router.
+# mrouted looks for it in "/etc/mrouted.conf".
+#
+# Command formats:
+#
+# name <boundname> <scoped-addr>/<mask-len>
+# cache_lifetime 3600 # seconds
+# pruning on
+#
+# phyint <local-addr> [disable] [metric <m>] [threshold <t>] [rate_limit <b>]
+# [boundary (<boundname>|<scoped-addr>/<mask-len>)]
+# [altnet (<subnet>/<mask-len>|<subnet>)]
+# tunnel <local-addr> <remote-addr> [srcrt] [metric <m>]
+# [threshold <t>] [rate_limit <b>]
+# [boundary (<boundname>|<scoped-addr>/<mask-len>)]
+#
+# NOTE: any phyint commands MUST precede any tunnel commands
+# NOTE: the mask-len is the no. of leading 1's in the mask
+# NOTE: rate_limit is in kilobits, and defaults to 500 for tunnels
+#
+# Example of named bounary:
+#name LOCAL 239.255.0.0/16
+#name EE 239.254.0.0/16 # i.e. the EE dept wants local groups
+#
+# Example of use of named boundary
+#phyint le1 boundary EE # le1 is our interface to comp sci,
+# # keep them away from our local groups
+#
+#
+# Template tunnel for mcast_install
+tunnel 128.4.0.77 128.4.0.8 metric 1 threshold 64 rate_limit 500 # <-- REPLACE
+# boundary LOCAL
+#
+# You might want to specify a boundary on your tunnel to the outside world,
+# as above.
+#
+# NOTE: ONLY uncomment the following if you are running mrouted.snmp!
+#sysName "mymrouter"
+#sysContact "Me <me@me.com> +x.yyy.zzz-zzzz"
+#sysVersion "MyOS 4.1.3 and mrouted"
+#sysLocation "The MBONE"
diff --git a/usr.sbin/mrouted/mrouted/Makefile b/usr.sbin/mrouted/mrouted/Makefile
new file mode 100644
index 0000000..afd3af8
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= mrouted
+MAN= mrouted.8
+SRCS= config.c cfparse.y main.c route.c vif.c prune.c callout.c rsrr.c \
+ ipip.c icmp.c vers.c igmp.c inet.c kern.c
+CLEANFILES+= vers.c
+
+CFLAGS+= -I$S
+YFLAGS=
+
+vers.c: $S/VERSION
+ rm -f vers.c ; \
+ sed 's/.*/char todaysversion[] = "&";/' $S/VERSION > vers.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mtrace.8 b/usr.sbin/mrouted/mtrace.8
new file mode 100644
index 0000000..44d99d5
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.8
@@ -0,0 +1,544 @@
+.\" Copyright (c) 1995 by the University of Southern California
+.\" All rights reserved.
+.\"
+.\" Permission to use, copy, modify, and distribute this software and its
+.\" documentation in source and binary forms for non-commercial purposes
+.\" and without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both the copyright notice and
+.\" this permission notice appear in supporting documentation, 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 Southern California, Information
+.\" Sciences Institute. The name of the University may not be used to
+.\" endorse or promote products derived from this software without
+.\" specific prior written permission.
+.\"
+.\" THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+.\" the suitability of this software for any purpose. 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.
+.\"
+.\" Other copyrights might apply to parts of this software and are so
+.\" noted when applicable.
+.\"
+.\" This manual page (but not the software) was derived from the
+.\" manual page for the traceroute program which bears the following
+.\" copyright notice:
+.\"
+.\" Copyright (c) 1988 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 8, 1995
+.Dt MTRACE 8
+.Os
+.Sh NAME
+.Nm mtrace
+.Nd print multicast path from a source to a receiver
+.Sh SYNOPSIS
+.Nm
+.Op Fl e Ar extrahops
+.Op Fl g Ar gateway
+.Op Fl i Ar if_addr
+.Op Fl l
+.Op Fl M
+.Op Fl m Ar max_hops
+.Op Fl n
+.Op Fl O
+.Op Fl p
+.Op Fl P
+.Op Fl q Ar nqueries
+.Op Fl r Ar resp_dest
+.Op Fl s
+.Op Fl S Ar stat_int
+.Op Fl t Ar ttl
+.Op Fl T
+.Op Fl U
+.Op Fl v
+.Op Fl w Ar waittime
+.Ar source
+.Op Ar receiver
+.Op Ar group
+.Sh DESCRIPTION
+Assessing problems in the distribution of IP multicast traffic
+can be difficult.
+The
+.Nm
+utility utilizes a tracing feature implemented in multicast routers that is
+accessed via an extension to the IGMP protocol. A trace query is
+passed hop-by-hop along the reverse path from the
+.Ar receiver
+to the
+.Ar source ,
+collecting hop addresses, packet counts, and routing error conditions
+along the path, and then the response is returned to the requestor.
+.Pp
+The only required parameter is the
+.Ar source
+host name or address. The default
+.Ar receiver
+is the host running mtrace, and the default
+.Ar group
+is 0.0.0.0, which is sufficient if packet loss
+statistics for a particular multicast group are not needed. These two
+optional parameters may be specified to test the path to some other
+receiver in a particular group, subject to some constraints as
+detailed below. The two parameters can be distinguished because the
+.Ar receiver
+is a unicast address and the
+.Ar group
+is a multicast address.
+If the
+.Fl g
+flag is specified, the source address defaults to the host running
+.Nm ,
+and the receiver defaults to the router being addressed with
+the
+.Fl g
+flag. In this case, there are no required parameters.
+.Pp
+NOTE: For Solaris 2.4/2.5, if the multicast interface is not the default
+interface, the
+.Fl i
+option must be used to set the local address.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl e Ar extrahops
+Try tracing
+.Ar extrahops
+hops past a non-responding router.
+.It Fl g Ar gwy
+Send the trace query via unicast directly to the multicast router
+.Ar gwy
+rather than multicasting the query.
+This must be the last-hop router on the path from the intended
+.Ar source
+to the
+.Ar receiver .
+.Pp
+.Em CAUTION ! !
+Versions 3.3 and 3.5 of
+.Nm mrouted
+will crash if a trace query is received via a
+unicast packet and
+.Nm mrouted
+has no route for the
+.Ar source
+address. Therefore, do not use the
+.Fl g
+option unless the target
+.Nm mrouted
+has been verified to be 3.4 or newer than 3.5.
+.It Fl i Ar addr
+Use
+.Ar addr
+as the local interface address (on a multi-homed host) for sending the
+trace query and as the default for the
+.Ar receiver
+and the response destination.
+.It Fl l
+Loop indefinitely printing packet rate and loss statistics for the
+multicast path every 10 seconds (see
+.Fl S Ar stat_int ) .
+.It Fl M
+Always request the response using multicast rather than attempting
+unicast for the last half of the tries.
+.It Fl m Ar n
+Set to
+.Ar n
+the maximum number of hops that will be traced from the
+.Ar receiver
+back toward the
+.Ar source .
+The default is 32 hops (infinity for the DVMRP routing protocol).
+.It Fl n
+Print hop addresses numerically rather than symbolically and numerically
+(saves a nameserver address-to-name lookup for each router found on the
+path).
+.It Fl q Ar n
+Set the maximum number of query attempts for any hop to
+.Ar n .
+The default is 3.
+.It Fl O
+Do not use the Router-Alert IP option on those requests which need it.
+Some versions of Cisco's IOS cannot handle
+multicast traceroutes with IP options, so it may be necessary to use the
+.Fl O
+flag if the last-hop router is a Cisco.
+.It Fl p
+Listen passively for multicast responses from traces initiated by
+others. This works best when run on a multicast router.
+.It Fl P
+Loop indefinitely collecting the path every 10 seconds (see
+.Fl S Ar stat_int )
+and printing it when it changes. Do not print any statistics.
+.It Fl r Ar host
+Send the trace response to
+.Ar host
+rather than to the host on which
+.Nm
+is being run, or to a multicast address other than the one registered
+for this purpose (224.0.1.32).
+.It Fl s
+Print a short form output including only the multicast path and not
+the packet rate and loss statistics.
+.It Fl S Ar n
+Change the interval between statistics gathering traces to
+.Ar n
+seconds (default 10 seconds).
+.It Fl t Ar ttl
+Set the
+.Ar ttl
+(time-to-live, or number of hops) for multicast trace queries and
+responses. The default is 127, except for local queries to the "all
+routers" multicast group which use ttl 1.
+.It Fl T
+"Tunnel statistics" mode; show loss rates for overall traffic.
+These statistics can be extremely misleading.
+.It Fl U
+Always request the response using unicast rather than attempting
+multicast first.
+.It Fl v
+Verbose mode; show hop times on the initial trace and statistics display.
+Also show the route that was used to forward the initial trace.
+.It Fl w Ar n
+Set the time to wait for a trace response to
+.Ar n
+seconds (default 3 seconds).
+.El
+.Sh USAGE
+.Ss "How It Works"
+The technique used by the
+.Nm traceroute
+utility to trace unicast network paths will not work for IP multicast
+because ICMP responses are specifically forbidden for multicast traffic.
+Instead, a tracing feature has been built into the multicast routers.
+This technique has the advantage that additional information about
+packet rates and losses can be accumulated while the number of packets
+sent is minimized.
+.Pp
+Since multicast uses
+reverse path forwarding, the trace is run backwards from the
+.Ar receiver
+to the
+.Ar source .
+A trace query packet is sent to the last
+hop multicast router (the leaf router for the desired
+.Ar receiver
+address). The last hop router builds a trace response packet, fills in
+a report for its hop, and forwards the trace packet using unicast to
+the router it believes is the previous hop for packets originating
+from the specified
+.Ar source .
+Each router along the path adds its report and forwards the packet.
+When the trace response packet reaches the first hop router (the router
+that is directly connected to the source's net), that router sends the
+completed response to the response destination address specified in
+the trace query.
+.Pp
+If some multicast router along the path does not implement the
+multicast traceroute feature or if there is some outage, then no
+response will be returned. To solve this problem, the trace query
+includes a maximum hop count field to limit the number of hops traced
+before the response is returned. That allows a partial path to be
+traced.
+.Pp
+The reports inserted by each router contain not only the address of
+the hop, but also the ttl required to forward and some flags to indicate
+routing errors, plus counts of the total number of packets on the
+incoming and outgoing interfaces and those forwarded for the specified
+.Ar group .
+Taking differences in these counts for two traces separated in time
+and comparing the output packet counts from one hop with the input
+packet counts of the next hop allows the calculation of packet rate
+and packet loss statistics for each hop to isolate congestion
+problems.
+.Ss Finding the Last-Hop Router
+The trace query must be sent to the multicast router which is the
+last hop on the path from the
+.Ar source
+to the
+.Ar receiver .
+If the receiver is on the local subnet (as determined using the subnet
+mask), then the default method is to multicast the trace query to
+all-routers.mcast.net (224.0.0.2) with a ttl of 1. Otherwise, the
+trace query is multicast to the
+.Ar group
+address since the last hop router will be a member of that group if
+the receiver is. Therefore it is necessary to specify a group that
+the intended receiver has joined. This multicast is sent with a
+default ttl of 127, which may not be sufficient for all cases (changed
+with the
+.Fl t
+option).
+If the last hop router is known, it may also be addressed directly
+using the
+.Fl g
+option). Alternatively, if it is desired to trace a group that the
+receiver has not joined, but it is known that the last-hop router is a
+member of another group, the
+.Fl g
+option may also be used to specify a different multicast address for the
+trace query.
+.Pp
+When tracing from a multihomed host or router, the default receiver
+address may not be the desired interface for the path from the source.
+In that case, the desired interface should be specified explicitly as
+the
+.Ar receiver .
+.Ss Directing the Response
+By default,
+.Nm
+first attempts to trace the full reverse path, unless the number of
+hops to trace is explicitly set with the
+.Fl m
+option. If there is no response within a 3 second timeout interval
+(changed with the
+.Fl w
+option), a "*" is printed and the probing switches to hop-by-hop mode.
+Trace queries are issued starting with a maximum hop count of one and
+increasing by one until the full path is traced or no response is
+received. At each hop, multiple probes are sent (default is three,
+changed with
+.Fl q
+option). The first half of the attempts (default is two) are made with
+the reply address set to standard multicast address, mtrace.mcast.net
+(224.0.1.32) with the ttl set to 32 more than what's needed to pass the
+thresholds seen so far along the path to the receiver. For each
+additional attempt, the ttl is increased by another 32 each time up to
+a maximum of 192. Since the desired router may not be able to send a
+multicast reply, the remainder of the attempts request that the
+response be sent via unicast to the host running
+.Nm .
+Alternatively, the multicast ttl may be set explicitly with the
+.Fl t
+option, the initial multicast attempts can be forced to use unicast
+instead with the
+.Fl U
+option, the final unicast attempts can be forced to use multicast
+instead with the
+.Fl M
+option, or if you specify
+.Fl UM ,
+.Nm
+will first attempt using unicast and then multicast. For each attempt,
+if no response is received within the timeout, a "*" is printed. After
+the specified number of attempts have failed,
+.Nm
+will try to query the next hop router with a DVMRP_ASK_NEIGHBORS2
+request (as used by the
+.Nm mrinfo
+program) to see what kind of router it is.
+The
+.Nm
+utility will try to query three (changed with the
+.Fl e
+option) hops past a non-responding router, in the hopes that even
+though it isn't capable of sending a response, it might be capable of
+forwarding the request on.
+.Sh EXAMPLES
+The output of
+.Nm
+is in two sections. The first section is a short listing of the hops
+in the order they are queried, that is, in the reverse of the order
+from the
+.Ar source
+to the
+.Ar receiver .
+For each hop, a line is printed showing the hop number (counted
+negatively to indicate that this is the reverse path); the multicast
+routing protocol (DVMRP, MOSPF, PIM, etc.); the threshold required to
+forward data (to the previous hop in the listing as indicated by the
+up-arrow character); and the cumulative delay for the query to reach
+that hop (valid only if the clocks are synchronized). This first
+section ends with a line showing the round-trip time which measures
+the interval from when the query is issued until the response is
+received, both derived from the local system clock, and the total
+ttl required for a packet to travel along this path. A sample use and
+output might be:
+.Pp
+.Bd -literal
+oak.isi.edu 80# mtrace -l caraway.lcs.mit.edu 224.2.0.3
+Mtrace from 18.26.0.170 to 128.9.160.100 via group 224.2.0.3
+Querying full reverse path...
+ 0 oak.isi.edu (128.9.160.100)
+ -1 cub.isi.edu (128.9.160.153) DVMRP thresh^ 1 3 ms
+ -2 la.dart.net (140.173.128.1) DVMRP thresh^ 1 14 ms
+ -3 dc.dart.net (140.173.64.1) DVMRP thresh^ 1 50 ms
+ -4 bbn.dart.net (140.173.32.1) DVMRP thresh^ 1 63 ms
+ -5 mit.dart.net (140.173.48.2) DVMRP thresh^ 1 71 ms
+ -6 caraway.lcs.mit.edu (18.26.0.170)
+Round trip time 124 ms; total ttl of 6 required.
+.Ed
+.Pp
+If a hop reports that it is using the default route to forward packets,
+the word
+.Em [default]
+is printed after that hop. If the
+.Fl v
+flag is supplied, the route being used to forward packets is printed
+in the form
+.Em [18.26.0/24] .
+.Pp
+The second section provides a pictorial view of the path in the
+forward direction with data flow indicated by arrows pointing downward
+and the query path indicated by arrows pointing upward. For each hop,
+both the entry and exit addresses of the router are shown if
+different, along with the initial ttl required on the packet in order
+to be forwarded at this hop and the propagation delay across the hop
+assuming that the routers at both ends have synchronized clocks.
+The right half of this section is composed of two sets of statistics.
+The first column contains the average packet rate for all traffic at
+each hop.
+The remaining columns are the
+number of packets lost, the number of packets sent, the percentage
+lost, and the average packet rate at each hop. These statistics are
+calculated from differences between traces and from hop to hop as
+explained above. The first group shows the statistics for all traffic
+flowing out the interface at one hop and in the interface at the next
+hop. The second group shows the statistics only for traffic forwarded
+from the specified
+.Ar source
+to the specified
+.Ar group .
+The first group of statistics may be expanded to include loss rates
+using the
+.Fl T
+option. However, these numbers can be extremely misleading and require
+detailed knowledge of the routers involved to be interpreted properly.
+.Pp
+These statistics are shown on one or two lines for each hop. Without
+any options, this second section of the output is printed only once,
+approximately 10 seconds after the initial trace. One line is shown
+for each hop showing the statistics over that 10-second period. If
+the
+.Fl l
+option is given, the second section is repeated every 10 seconds and
+two lines are shown for each hop. The first line shows the statistics
+for the last 10 seconds, and the second line shows the cumulative
+statistics over the period since the initial trace, which is 101
+seconds in the example below. The second section of the output is
+omitted if the
+.Fl s
+option is set or if no multicast group is specified.
+.Pp
+.Bd -literal
+Waiting to accumulate statistics... Results after 101 seconds:
+
+ Source Response Dest Overall Packet Statistics For Traffic From
+18.26.0.170 128.9.160.100 Packet 18.26.0.170 To 224.2.0.3
+ | __/ rtt 125 ms Rate Lost/Sent = Pct Rate
+ v / hop 65 ms ------- ---------------------
+18.26.0.144
+140.173.48.2 mit.dart.net
+ | ^ ttl 1 0 pps 0/2 = --% 0 pps
+ v | hop 8 ms 0 pps 0/18 = 0% 0 pps
+140.173.48.1
+140.173.32.1 bbn.dart.net
+ | ^ ttl 2 0 pps 0/2 = --% 0 pps
+ v | hop 12 ms 0 pps 0/18 = 0% 0 pps
+140.173.32.2
+140.173.64.1 dc.dart.net
+ | ^ ttl 3 27 pps 0/2 = --% 0 pps
+ v | hop 34 ms 26 pps 0/18 = 0% 0 pps
+140.173.64.2
+140.173.128.1 la.dart.net
+ | ^ ttl 4 83 pps 0/2 = --% 0 pps
+ v | hop 11 ms 79 pps 0/18 = 0% 0 pps
+140.173.128.2
+128.9.160.153 cub.isi.edu
+ | \\__ ttl 5 83 pps ?/2 0 pps
+ v \\ hop -8 ms 79 pps ?/18 0 pps
+128.9.160.100 128.9.160.100
+ Receiver Query Source
+.Ed
+.Pp
+Because the packet counts may be changing as the trace query is
+propagating, there may be small errors (off by 1 or 2) in these
+statistics. However, those errors should not accumulate, so the
+cumulative statistics line should increase in accuracy as a new trace
+is run every 10 seconds. There are two sources of larger errors, both
+of which show up as negative losses:
+.Pp
+If the input to a node is from a multi-access network with more than
+one other node attached, then the input count will be (close to) the
+sum of the output counts from all the attached nodes, but the output
+count from the previous hop on the traced path will be only part of
+that. Hence the output count minus the input count will be negative.
+.Pp
+In release 3.3 of the DVMRP multicast forwarding software for SunOS
+and other systems, a multicast packet generated on a router will be
+counted as having come in an interface even though it did not. This
+creates the negative loss that can be seen in the example above.
+.Pp
+Note that these negative losses may mask positive losses.
+.Pp
+In the example, there is also one negative hop time. This simply
+indicates a lack of synchronization between the system clocks across
+that hop. This example also illustrates how the percentage loss is
+shown as two dashes when the number of packets sent is less than 10
+because the percentage would not be statistically valid.
+.Pp
+A second example shows a trace to a receiver that is not local; the
+query is sent to the last-hop router with the
+.Fl g
+option. In this example, the trace of the full reverse path resulted
+in no response because there was a node running an old version of
+.Nm mrouted
+that did not implement the multicast traceroute function, so
+.Nm
+switched to hop-by-hop mode. The
+.Dq Output pruned
+error code
+indicates that traffic for group 224.2.143.24 would not be forwarded.
+.Pp
+.Bd -literal
+oak.isi.edu 108# mtrace -g 140.173.48.2 204.62.246.73 \\
+ butter.lcs.mit.edu 224.2.143.24
+Mtrace from 204.62.246.73 to 18.26.0.151 via group 224.2.143.24
+Querying full reverse path... * switching to hop-by-hop:
+ 0 butter.lcs.mit.edu (18.26.0.151)
+ -1 jam.lcs.mit.edu (18.26.0.144) DVMRP thresh^ 1 33 ms Output pruned
+ -2 bbn.dart.net (140.173.48.1) DVMRP thresh^ 1 36 ms
+ -3 dc.dart.net (140.173.32.2) DVMRP thresh^ 1 44 ms
+ -4 darpa.dart.net (140.173.240.2) DVMRP thresh^ 16 47 ms
+ -5 * * * noc.hpc.org (192.187.8.2) [mrouted 2.2] didn't respond
+Round trip time 95 ms
+.Ed
+.Sh AUTHORS
+.An -nosplit
+Implemented by
+.An Steve Casner
+based on an initial prototype written by
+.An Ajit Thyagarajan .
+The multicast traceroute mechanism was designed by
+.An Van Jacobson
+with help from
+.An Steve Casner ,
+.An Steve Deering ,
+.An Dino Farinacci ,
+and
+.An Deb Agrawal ;
+it was implemented in
+.Nm mrouted
+by
+.An Ajit Thyagarajan
+and
+.An Bill Fenner .
+The option syntax and the output format of
+.Nm
+are modeled after the unicast
+.Nm traceroute
+program written by
+.An Van Jacobson .
+.Sh SEE ALSO
+.Xr map-mbone 8 ,
+.Xr mrinfo 8 ,
+.Xr mrouted 8 ,
+.Xr traceroute 8
+.Sh BUGS
+Statistics collection in passive mode doesn't always produce the same output
+as when actively collecting data.
diff --git a/usr.sbin/mrouted/mtrace.c b/usr.sbin/mrouted/mtrace.c
new file mode 100644
index 0000000..9cb53e3
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.c
@@ -0,0 +1,3177 @@
+/*
+ * mtrace.c
+ *
+ * This tool traces the branch of a multicast tree from a source to a
+ * receiver for a particular multicast group and gives statistics
+ * about packet rate and loss for each hop along the path. It can
+ * usually be invoked just as
+ *
+ * mtrace source
+ *
+ * to trace the route from that source to the local host for a default
+ * group when only the route is desired and not group-specific packet
+ * counts. See the usage line for more complex forms.
+ *
+ *
+ * Released 4 Apr 1995. This program was adapted by Steve Casner
+ * (USC/ISI) from a prototype written by Ajit Thyagarajan (UDel and
+ * Xerox PARC). It attempts to parallel in command syntax and output
+ * format the unicast traceroute program written by Van Jacobson (LBL)
+ * for the parts where that makes sense.
+ *
+ * Copyright (c) 1995 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for any purposes and without
+ * fee is hereby granted, provided that the above copyright notice
+ * appear in all copies and that both the copyright notice and this
+ * permission notice appear in supporting documentation, 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 Southern California, Information Sciences
+ * Institute. The name of the University may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. 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.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ *
+ * Parts of this software are derived from mrouted, which has the
+ * following license:
+ *
+ * The mrouted program is covered by the following license. Use of the
+ * mrouted program represents acceptance of these terms and conditions.
+ *
+ * 1. STANFORD grants to LICENSEE a nonexclusive and nontransferable
+ * license to use, copy and modify the computer software ``mrouted''
+ * (hereinafter called the ``Program''), upon the terms and conditions
+ * hereinafter set out and until Licensee discontinues use of the Licensed
+ * Program.
+ *
+ * 2. LICENSEE acknowledges that the Program is a research tool still in
+ * the development state, that it is being supplied ``as is,'' without any
+ * accompanying services from STANFORD, and that this license is entered
+ * into in order to encourage scientific collaboration aimed at further
+ * development and application of the Program.
+ *
+ * 3. LICENSEE may copy the Program and may sublicense others to use
+ * object code copies of the Program or any derivative version of the
+ * Program. All copies must contain all copyright and other proprietary
+ * notices found in the Program as provided by STANFORD. Title to
+ * copyright to the Program remains with STANFORD.
+ *
+ * 4. LICENSEE may create derivative versions of the Program. LICENSEE
+ * hereby grants STANFORD a royalty-free license to use, copy, modify,
+ * distribute and sublicense any such derivative works. At the time
+ * LICENSEE provides a copy of a derivative version of the Program to a
+ * third party, LICENSEE shall provide STANFORD with one copy of the
+ * source code of the derivative version at no charge to STANFORD.
+ *
+ * 5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+ * IMPLIED. By way of example, but not limitation, STANFORD MAKES NO
+ * REPRESENTATION OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
+ * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED PROGRAM WILL NOT
+ * INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD
+ * shall not be held liable for any liability nor for any direct, indirect
+ * or consequential damages with respect to any claim by LICENSEE or any
+ * third party on account of or arising from this Agreement or use of the
+ * Program.
+ *
+ * 6. This agreement shall be construed, interpreted and applied in
+ * accordance with the State of California and any legal action arising
+ * out of this Agreement or use of the Program shall be filed in a court
+ * in the State of California.
+ *
+ * 7. Nothing in this Agreement shall be construed as conferring rights to
+ * use in advertising, publicity or otherwise any trademark or the name
+ * of ``Stanford''.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * The mtrace program has been modified and improved by Xerox
+ * Corporation. Xerox grants to LICENSEE a non-exclusive and
+ * non-transferable license to use, copy, and modify the Xerox modified
+ * and improved mrouted software on the same terms and conditions which
+ * govern the license Stanford and ISI grant with respect to the mtrace
+ * program. These terms and conditions are incorporated in this grant
+ * by reference and shall be deemed to have been accepted by LICENSEE
+ * to cover its relationship with Xerox Corporation with respect to any
+ * use of the Xerox improved program.
+ *
+ * The mtrace program is COPYRIGHT 1998 by Xerox Corporation.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <memory.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/igmp.h>
+#include <sys/ioctl.h>
+#ifdef SYSV
+#include <sys/sockio.h>
+#endif
+#include <arpa/inet.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#ifdef SUNOS5
+#include <sys/systeminfo.h>
+#endif
+
+typedef unsigned int u_int32; /* XXX */
+#include "mtrace.h"
+
+#define DEFAULT_TIMEOUT 3 /* How long to wait before retrying requests */
+#define DEFAULT_RETRIES 3 /* How many times to try */
+#define DEFAULT_EXTRAHOPS 3 /* How many hops past a non-responding rtr */
+#define MAXHOPS 60 /* Don't need more hops than this */
+#define UNICAST_TTL 255 /* TTL for unicast response */
+#define MULTICAST_TTL1 127 /* Default TTL for multicast query/response */
+#define MULTICAST_TTL_INC 32 /* TTL increment for increase after timeout */
+#define MULTICAST_TTL_MAX 192 /* Maximum TTL allowed (protect low-BW links */
+
+#define TRUE 1
+#define FALSE 0
+#define DVMRP_ASK_NEIGHBORS2 5 /* DVMRP msg requesting neighbors */
+#define DVMRP_NEIGHBORS2 6 /* reply to above */
+#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
+#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
+#define MAX_IP_PACKET_LEN 576
+#define MIN_IP_HEADER_LEN 20
+#define MAX_IP_HEADER_LEN 60
+#define MAX_DVMRP_DATA_LEN \
+ ( MAX_IP_PACKET_LEN - MAX_IP_HEADER_LEN - IGMP_MINLEN )
+
+struct resp_buf {
+ u_long qtime; /* Time query was issued */
+ u_long rtime; /* Time response was received */
+ int len; /* Number of reports or length of data */
+ struct igmp igmp; /* IGMP header */
+ union {
+ struct {
+ struct tr_query q; /* Query/response header */
+ struct tr_resp r[MAXHOPS]; /* Per-hop reports */
+ } t;
+ char d[MAX_DVMRP_DATA_LEN]; /* Neighbor data */
+ } u;
+} base, incr[2];
+
+#define qhdr u.t.q
+#define resps u.t.r
+#define ndata u.d
+
+char *names[MAXHOPS];
+
+/*
+ * In mrouted 3.3 and 3.4 (and in some Cisco IOS releases),
+ * cache entries can get deleted even if there is traffic
+ * flowing, which will reset the per-source/group counters.
+ */
+#define BUG_RESET 0x01
+
+/*
+ * Also in mrouted 3.3 and 3.4, there's a bug in neighbor
+ * version processing which can cause them to believe that
+ * the neighbor is constantly resetting. This causes them
+ * to constantly delete all their state.
+ */
+#define BUG_RESET2X 0x02
+
+/*
+ * Pre-3.7 mrouted's forget to byte-swap their reports.
+ */
+#define BUG_SWAP 0x04
+
+/*
+ * Pre-3.9 mrouted's forgot a parenthesis in the htonl()
+ * on the time calculation so supply bogus times.
+ */
+#define BUG_BOGUSTIME 0x08
+
+#define BUG_NOPRINT (BUG_RESET | BUG_RESET2X)
+
+int bugs[MAXHOPS]; /* List of bugs noticed at each hop */
+
+struct mtrace {
+ struct mtrace *next;
+ struct resp_buf base, incr[2];
+ struct resp_buf *new, *prev;
+ int nresp;
+ struct timeval last;
+ int bugs[MAXHOPS];
+ char *names[MAXHOPS];
+ int lastqid;
+};
+
+int timeout = DEFAULT_TIMEOUT;
+int nqueries = DEFAULT_RETRIES;
+int numeric = FALSE;
+int debug = 0;
+int passive = FALSE;
+int multicast = FALSE;
+int unicast = FALSE;
+int statint = 10;
+int verbose = FALSE;
+int tunstats = FALSE;
+int weak = FALSE;
+int extrahops = DEFAULT_EXTRAHOPS;
+int printstats = TRUE;
+int sendopts = TRUE;
+int lossthresh = 0;
+int fflag = FALSE;
+int staticqid = 0;
+
+u_int32 defgrp; /* Default group if not specified */
+u_int32 query_cast; /* All routers multicast addr */
+u_int32 resp_cast; /* Mtrace response multicast addr */
+
+u_int32 lcl_addr = 0; /* This host address, in NET order */
+u_int32 dst_netmask = 0; /* netmask to go with qdst */
+
+/*
+ * Query/response parameters, all initialized to zero and set later
+ * to default values or from options.
+ */
+u_int32 qsrc = 0; /* Source address in the query */
+u_int32 qgrp = 0; /* Group address in the query */
+u_int32 qdst = 0; /* Destination (receiver) address in query */
+u_char qno = 0; /* Max number of hops to query */
+u_int32 raddr = 0; /* Address where response should be sent */
+int qttl = 0; /* TTL for the query packet */
+u_char rttl = 0; /* TTL for the response packet */
+u_int32 gwy = 0; /* User-supplied last-hop router address */
+u_int32 tdst = 0; /* Address where trace is sent (last-hop) */
+
+char s1[19]; /* buffers to hold the string representations */
+char s2[19]; /* of IP addresses, to be passed to inet_fmt() */
+char s3[19]; /* or inet_fmts(). */
+
+#if !(defined(BSD) && (BSD >= 199103))
+extern int errno;
+extern int sys_nerr;
+extern char * sys_errlist[];
+#endif
+
+#define RECV_BUF_SIZE 8192
+char *send_buf, *recv_buf;
+int igmp_socket;
+u_int32 allrtrs_group;
+char router_alert[4]; /* Router Alert IP Option */
+#ifndef IPOPT_RA
+#define IPOPT_RA 148
+#endif
+#ifdef SUNOS5
+char eol[4]; /* EOL IP Option */
+int ip_addlen = 0; /* Workaround for Option bug #2 */
+#endif
+
+/*
+ * max macro, with weird case to avoid conflicts
+ */
+#define MaX(a,b) ((a) > (b) ? (a) : (b))
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+typedef int (*callback_t) __P((int, u_char *, int, struct igmp *, int,
+ struct sockaddr *, int *, struct timeval *));
+
+void init_igmp __P((void));
+void send_igmp __P((u_int32 src, u_int32 dst, int type,
+ int code, u_int32 group,
+ int datalen));
+int inet_cksum __P((u_short *addr, u_int len));
+void k_set_rcvbuf __P((int bufsize));
+void k_hdr_include __P((int bool));
+void k_set_ttl __P((int t));
+void k_set_loop __P((int l));
+void k_set_if __P((u_int32 ifa));
+void k_join __P((u_int32 grp, u_int32 ifa));
+void k_leave __P((u_int32 grp, u_int32 ifa));
+char * inet_fmt __P((u_int32 addr, char *s));
+char * inet_fmts __P((u_int32 addr, u_int32 mask, char *s));
+char * inet_name __P((u_int32 addr));
+u_int32 host_addr __P((char *name));
+/* u_int is promoted u_char */
+char * proto_type __P((u_int type));
+char * flag_type __P((u_int type));
+
+u_int32 get_netmask __P((int s, u_int32 *dst));
+int get_ttl __P((struct resp_buf *buf));
+int t_diff __P((u_long a, u_long b));
+u_long byteswap __P((u_long v));
+int mtrace_callback __P((int, u_char *, int, struct igmp *,
+ int, struct sockaddr *, int *,
+ struct timeval *));
+int send_recv __P((u_int32 dst, int type, int code,
+ int tries, struct resp_buf *save,
+ callback_t callback));
+void passive_mode __P((void));
+char * print_host __P((u_int32 addr));
+char * print_host2 __P((u_int32 addr1, u_int32 addr2));
+void print_trace __P((int idx, struct resp_buf *buf,
+ char **names));
+int what_kind __P((struct resp_buf *buf, char *why));
+char * scale __P((int *hop));
+void stat_line __P((struct tr_resp *r, struct tr_resp *s,
+ int have_next, int *res));
+void fixup_stats __P((struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new,
+ int *bugs));
+int check_thresh __P((int thresh,
+ struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new));
+int print_stats __P((struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new,
+ int *bugs,
+ char **names));
+int path_changed __P((struct resp_buf *base,
+ struct resp_buf *new));
+void check_vif_state __P((void));
+
+int main __P((int argc, char *argv[]));
+void log __P((int, int, char *, ...));
+static void usage __P((void));
+
+
+/*
+ * Open and initialize the igmp socket, and fill in the non-changing
+ * IP header fields in the output packet buffer.
+ */
+void
+init_igmp()
+{
+ struct ip *ip;
+
+ recv_buf = (char *)malloc(RECV_BUF_SIZE);
+ if (recv_buf == 0)
+ log(LOG_ERR, 0, "Out of memory allocating recv_buf!");
+ send_buf = (char *)malloc(RECV_BUF_SIZE);
+ if (send_buf == 0)
+ log(LOG_ERR, 0, "Out of memory allocating send_buf!");
+
+ if ((igmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP)) < 0)
+ log(LOG_ERR, errno, "IGMP socket");
+
+ k_hdr_include(TRUE); /* include IP header when sending */
+ k_set_rcvbuf(48*1024); /* lots of input buffering */
+ k_set_ttl(1); /* restrict multicasts to one hop */
+ k_set_loop(FALSE); /* disable multicast loopback */
+
+ ip = (struct ip *)send_buf;
+ ip->ip_hl = sizeof(struct ip) >> 2;
+ ip->ip_v = IPVERSION;
+ ip->ip_tos = 0;
+ ip->ip_off = 0;
+ ip->ip_p = IPPROTO_IGMP;
+ ip->ip_ttl = MAXTTL; /* applies to unicasts only */
+
+#ifndef INADDR_ALLRTRS_GROUP
+#define INADDR_ALLRTRS_GROUP 0xe0000002 /* 224.0.0.2 */
+#endif
+ allrtrs_group = htonl(INADDR_ALLRTRS_GROUP);
+
+ router_alert[0] = IPOPT_RA; /* Router Alert */
+ router_alert[1] = 4; /* 4 bytes */
+ router_alert[2] = 0;
+ router_alert[3] = 0;
+}
+
+#ifdef SUNOS5
+void
+checkforsolarisbug()
+{
+ u_int32 localhost = htonl(0x7f000001);
+
+ eol[0] = IPOPT_EOL;
+ eol[1] = IPOPT_EOL;
+ eol[2] = IPOPT_EOL;
+ eol[3] = IPOPT_EOL;
+
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, eol, sizeof(eol));
+ /*
+ * Check if the kernel adds the options length to the packet
+ * length. Send myself an IGMP packet of type 0 (illegal),
+ * with 4 IPOPT_EOL options, my PID (for collision detection)
+ * and 4 bytes of zero (so that the checksum works whether
+ * the 4 bytes of zero get truncated or not).
+ */
+ bzero(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN, 8);
+ *(int *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN) = getpid();
+ send_igmp(localhost, localhost, 0, 0, 0, 8);
+ while (1) {
+ int recvlen, dummy = 0;
+
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, NULL, &dummy);
+ /* 8 == 4 bytes of options and 4 bytes of PID */
+ if (recvlen >= MIN_IP_HEADER_LEN + IGMP_MINLEN + 8) {
+ struct ip *ip = (struct ip *)recv_buf;
+ struct igmp *igmp;
+ int *p;
+
+ if (ip->ip_hl != 6 ||
+ ip->ip_p != IPPROTO_IGMP ||
+ ip->ip_src.s_addr != localhost ||
+ ip->ip_dst.s_addr != localhost)
+ continue;
+
+ igmp = (struct igmp *)(recv_buf + (ip->ip_hl << 2));
+ if (igmp->igmp_group.s_addr != 0)
+ continue;
+ if (igmp->igmp_type != 0 || igmp->igmp_code != 0)
+ continue;
+
+ p = (int *)((char *)igmp + IGMP_MINLEN);
+ if (*p != getpid())
+ continue;
+
+#ifdef RAW_INPUT_IS_RAW
+ ip->ip_len = ntohs(ip->ip_len);
+#endif
+ if (ip->ip_len == IGMP_MINLEN + 4)
+ ip_addlen = 4;
+ else if (ip->ip_len == IGMP_MINLEN + 8)
+ ip_addlen = 0;
+ else
+ log(LOG_ERR, 0, "while checking for Solaris bug: Sent %d bytes and got back %d!", IGMP_MINLEN + 8, ip->ip_len);
+
+ break;
+ }
+ }
+}
+#endif
+
+/*
+ * Construct an IGMP message in the output packet buffer. The caller may
+ * have already placed data in that buffer, of length 'datalen'. Then send
+ * the message from the interface with IP address 'src' to destination 'dst'.
+ */
+void
+send_igmp(src, dst, type, code, group, datalen)
+ u_int32 src, dst;
+ int type, code;
+ u_int32 group;
+ int datalen;
+{
+ struct sockaddr_in sdst;
+ struct ip *ip;
+ struct igmp *igmp;
+ int setloop = 0;
+ static int raset = 0;
+ int sendra = 0;
+ int sendlen;
+
+ ip = (struct ip *)send_buf;
+ ip->ip_src.s_addr = src;
+ ip->ip_dst.s_addr = dst;
+ ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+ sendlen = ip->ip_len;
+#ifdef SUNOS5
+ ip->ip_len += ip_addlen;
+#endif
+#ifdef RAW_OUTPUT_IS_RAW
+ ip->ip_len = htons(ip->ip_len);
+#endif
+
+ igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
+ igmp->igmp_type = type;
+ igmp->igmp_code = code;
+ igmp->igmp_group.s_addr = group;
+ igmp->igmp_cksum = 0;
+ igmp->igmp_cksum = inet_cksum((u_short *)igmp,
+ IGMP_MINLEN + datalen);
+
+ if (IN_MULTICAST(ntohl(dst))) {
+ k_set_if(src);
+ setloop = 1;
+ k_set_loop(TRUE);
+ if (dst != allrtrs_group)
+ sendra = 1;
+ }
+
+ if (sendopts && sendra && !raset) {
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ router_alert, sizeof(router_alert));
+ raset = 1;
+ } else if (!sendra && raset) {
+#ifdef SUNOS5
+ /*
+ * SunOS5 < 5.6 cannot properly reset the IP_OPTIONS "socket"
+ * option. Instead, set up a string of 4 EOL's.
+ */
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ eol, sizeof(eol));
+#else
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ NULL, 0);
+#endif
+ raset = 0;
+ }
+
+ bzero(&sdst, sizeof(sdst));
+ sdst.sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ sdst.sin_len = sizeof(sdst);
+#endif
+ sdst.sin_addr.s_addr = dst;
+ if (sendto(igmp_socket, send_buf, sendlen, 0,
+ (struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
+ log(LOG_WARNING, errno, "sendto to %s on %s",
+ inet_fmt(dst, s1), inet_fmt(src, s2));
+ }
+
+ if (setloop)
+ k_set_loop(FALSE);
+
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
+ type == IGMP_MTRACE ? "mtrace request" : "ask_neighbors",
+ src == INADDR_ANY ? "INADDR_ANY" : inet_fmt(src, s1),
+ inet_fmt(dst, s2));
+}
+
+/*
+ * inet_cksum extracted from:
+ * P I N G . C
+ *
+ * Author -
+ * Mike Muuss
+ * U. S. Army Ballistic Research Laboratory
+ * December, 1983
+ * Modified at Uc Berkeley
+ *
+ * (ping.c) Status -
+ * Public Domain. Distribution Unlimited.
+ *
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ */
+int
+inet_cksum(addr, len)
+ u_short *addr;
+ u_int len;
+{
+ register int nleft = (int)len;
+ register u_short *w = addr;
+ u_short answer = 0;
+ 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) {
+ *(u_char *) (&answer) = *(u_char *)w ;
+ sum += answer;
+ }
+
+ /*
+ * 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);
+}
+
+void
+k_set_rcvbuf(bufsize)
+ int bufsize;
+{
+ if (setsockopt(igmp_socket, SOL_SOCKET, SO_RCVBUF,
+ (char *)&bufsize, sizeof(bufsize)) < 0)
+ log(LOG_ERR, errno, "setsockopt SO_RCVBUF %u", bufsize);
+}
+
+
+void
+k_hdr_include(bool)
+ int bool;
+{
+#ifdef IP_HDRINCL
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL,
+ (char *)&bool, sizeof(bool)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", bool);
+#endif
+}
+
+void
+k_set_ttl(t)
+ int t;
+{
+ u_char ttl;
+
+ ttl = t;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&ttl, sizeof(ttl)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_TTL %u", ttl);
+}
+
+
+void
+k_set_loop(l)
+ int l;
+{
+ u_char loop;
+
+ loop = l;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&loop, sizeof(loop)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_LOOP %u", loop);
+}
+
+void
+k_set_if(ifa)
+ u_int32 ifa;
+{
+ struct in_addr adr;
+
+ adr.s_addr = ifa;
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&adr, sizeof(adr)) < 0)
+ log(LOG_ERR, errno, "setsockopt IP_MULTICAST_IF %s",
+ inet_fmt(ifa, s1));
+}
+
+void
+k_join(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't join group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+
+void
+k_leave(grp, ifa)
+ u_int32 grp;
+ u_int32 ifa;
+{
+ struct ip_mreq mreq;
+
+ mreq.imr_multiaddr.s_addr = grp;
+ mreq.imr_interface.s_addr = ifa;
+
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ log(LOG_WARNING, errno, "can't leave group %s on interface %s",
+ inet_fmt(grp, s1), inet_fmt(ifa, s2));
+}
+
+/*
+ * Convert an IP address in u_long (network) format into a printable string.
+ */
+char *
+inet_fmt(addr, s)
+ u_int32 addr;
+ char *s;
+{
+ register u_char *a;
+
+ a = (u_char *)&addr;
+ sprintf(s, "%u.%u.%u.%u", a[0], a[1], a[2], a[3]);
+ return (s);
+}
+
+
+/*
+ * Convert an IP subnet number in u_long (network) format into a printable
+ * string including the netmask as a number of bits.
+ */
+char *
+inet_fmts(addr, mask, s)
+ u_int32 addr, mask;
+ char *s;
+{
+ register u_char *a, *m;
+ int bits;
+
+ if ((addr == 0) && (mask == 0)) {
+ sprintf(s, "default");
+ return (s);
+ }
+ a = (u_char *)&addr;
+ m = (u_char *)&mask;
+ bits = 33 - ffs(ntohl(mask));
+
+ if (m[3] != 0) sprintf(s, "%u.%u.%u.%u/%d", a[0], a[1], a[2], a[3],
+ bits);
+ else if (m[2] != 0) sprintf(s, "%u.%u.%u/%d", a[0], a[1], a[2], bits);
+ else if (m[1] != 0) sprintf(s, "%u.%u/%d", a[0], a[1], bits);
+ else sprintf(s, "%u/%d", a[0], bits);
+
+ return (s);
+}
+
+char *
+inet_name(addr)
+ u_int32 addr;
+{
+ struct hostent *e;
+
+ e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
+
+ return e ? e->h_name : "?";
+}
+
+
+u_int32
+host_addr(name)
+ char *name;
+{
+ struct hostent *e = (struct hostent *)0;
+ u_int32 addr;
+ int i, dots = 3;
+ char buf[40];
+ char *ip = name;
+ char *op = buf;
+
+ /*
+ * Undo BSD's favor -- take fewer than 4 octets as net/subnet address
+ * if the name is all numeric.
+ */
+ for (i = sizeof(buf) - 7; i > 0; --i) {
+ if (*ip == '.') --dots;
+ else if (*ip == '\0') break;
+ else if (!isdigit(*ip)) dots = 0; /* Not numeric, don't add zeroes */
+ *op++ = *ip++;
+ }
+ for (i = 0; i < dots; ++i) {
+ *op++ = '.';
+ *op++ = '0';
+ }
+ *op = '\0';
+
+ if (dots <= 0)
+ e = gethostbyname(name);
+ if (e && (e->h_length == sizeof(addr))) {
+ memcpy((char *)&addr, e->h_addr_list[0], e->h_length);
+ if (e->h_addr_list[1])
+ fprintf(stderr, "Warning: %s has multiple addresses, using %s\n",
+ name, inet_fmt(addr, s1));
+ } else {
+ addr = inet_addr(buf);
+ if (addr == -1 || (IN_MULTICAST(addr) && dots)) {
+ addr = 0;
+ printf("Could not parse %s as host name or address\n", name);
+ }
+ }
+ return addr;
+}
+
+
+char *
+proto_type(type)
+ u_int type;
+{
+ static char buf[80];
+
+ switch (type) {
+ case PROTO_DVMRP:
+ return ("DVMRP");
+ case PROTO_MOSPF:
+ return ("MOSPF");
+ case PROTO_PIM:
+ return ("PIM");
+ case PROTO_CBT:
+ return ("CBT");
+ case PROTO_PIM_SPECIAL:
+ return ("PIM/Special");
+ case PROTO_PIM_STATIC:
+ return ("PIM/Static");
+ case PROTO_DVMRP_STATIC:
+ return ("DVMRP/Static");
+ case PROTO_PIM_BGP4PLUS:
+ return ("PIM/BGP4+");
+ case PROTO_CBT_SPECIAL:
+ return ("CBT/Special");
+ case PROTO_CBT_STATIC:
+ return ("CBT/Static");
+ case PROTO_PIM_ASSERT:
+ return ("PIM/Assert");
+ case 0:
+ return ("None");
+ default:
+ (void) sprintf(buf, "Unknown protocol code %d", type);
+ return (buf);
+ }
+}
+
+
+char *
+flag_type(type)
+ u_int type;
+{
+ static char buf[80];
+
+ switch (type) {
+ case TR_NO_ERR:
+ return ("");
+ case TR_WRONG_IF:
+ return ("Wrong interface");
+ case TR_PRUNED:
+ return ("Prune sent upstream");
+ case TR_OPRUNED:
+ return ("Output pruned");
+ case TR_SCOPED:
+ return ("Hit scope boundary");
+ case TR_NO_RTE:
+ return ("No route");
+ case TR_NO_FWD:
+ return ("Not forwarding");
+ case TR_HIT_RP:
+ return ("Reached RP/Core");
+ case TR_RPF_IF:
+ return ("RPF Interface");
+ case TR_NO_MULTI:
+ return ("Multicast disabled");
+ case TR_OLD_ROUTER:
+ return ("Next router no mtrace");
+ case TR_NO_SPACE:
+ return ("No space in packet");
+ case TR_ADMIN_PROHIB:
+ return ("Admin. Prohibited");
+ default:
+ (void) sprintf(buf, "Unknown error code %d", type);
+ return (buf);
+ }
+}
+
+/*
+ * If destination is on a local net, get the netmask, else set the
+ * netmask to all ones. There are two side effects: if the local
+ * address was not explicitly set, and if the destination is on a
+ * local net, use that one; in either case, verify that the local
+ * address is valid.
+ */
+u_int32
+get_netmask(s, dst)
+ int s;
+ u_int32 *dst;
+{
+ unsigned int n;
+ struct ifconf ifc;
+ struct ifreq *ifrp, *ifend;
+ u_int32 if_addr, if_mask;
+ u_int32 retval = 0xFFFFFFFF;
+ int found = FALSE;
+ int num_ifreq = 32;
+
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = malloc(ifc.ifc_len);
+ while (ifc.ifc_buf) {
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ perror("ioctl SIOCGIFCONF");
+ return retval;
+ }
+
+ /*
+ * If the buffer was large enough to hold all the addresses
+ * then break out, otherwise increase the buffer size and
+ * try again.
+ *
+ * The only way to know that we definitely had enough space
+ * is to know that there was enough space for at least one
+ * more struct ifreq. ???
+ */
+ if ((num_ifreq * sizeof(struct ifreq)) >=
+ ifc.ifc_len + sizeof(struct ifreq))
+ break;
+
+ num_ifreq *= 2;
+ ifc.ifc_len = num_ifreq * sizeof(struct ifreq);
+ ifc.ifc_buf = realloc(ifc.ifc_buf, ifc.ifc_len);
+ }
+ if (ifc.ifc_buf == NULL) {
+ fprintf(stderr, "getting interface list: ran out of memory");
+ exit(1);
+ }
+
+ ifrp = (struct ifreq *)ifc.ifc_buf;
+ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
+ /*
+ * Loop through all of the interfaces.
+ */
+ for (; ifrp < ifend && !found; ifrp = (struct ifreq *)((char *)ifrp + n)) {
+#if BSD >= 199006
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+#else
+ n = sizeof(*ifrp);
+#endif
+ /*
+ * Ignore any interface for an address family other than IP.
+ */
+ if (ifrp->ifr_addr.sa_family != AF_INET)
+ continue;
+
+ if_addr = ((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr.s_addr;
+ if (ioctl(s, SIOCGIFFLAGS, (char *)ifrp) < 0) {
+ fprintf(stderr, "SIOCGIFFLAGS on ");
+ perror(ifrp->ifr_name);
+ continue;
+ }
+ if ((ifrp->ifr_flags & (IFF_MULTICAST|IFF_UP|IFF_LOOPBACK)) !=
+ (IFF_MULTICAST|IFF_UP))
+ continue;
+ if (*dst == 0)
+ *dst = if_addr;
+ if (ioctl(s, SIOCGIFNETMASK, (char *)ifrp) >= 0) {
+ if_mask = ((struct sockaddr_in *)&(ifrp->ifr_addr))->sin_addr.s_addr;
+ if (if_mask != 0 && (*dst & if_mask) == (if_addr & if_mask)) {
+ retval = if_mask;
+ if (lcl_addr == 0) lcl_addr = if_addr; /* XXX what about aliases? */
+ }
+ }
+ if (lcl_addr == if_addr) found = TRUE;
+ }
+ if (!found && lcl_addr != 0) {
+ printf("Interface address is not valid\n");
+ exit(1);
+ }
+ return (retval);
+}
+
+
+/*
+ * Try to pick a TTL that will get past all the thresholds in the path.
+ */
+int
+get_ttl(buf)
+ struct resp_buf *buf;
+{
+ int rno;
+ struct tr_resp *b;
+ u_int ttl;
+
+ if (buf && (rno = buf->len) > 0) {
+ b = buf->resps + rno - 1;
+ ttl = b->tr_fttl;
+
+ while (--rno > 0) {
+ --b;
+ if (ttl < b->tr_fttl) ttl = b->tr_fttl;
+ else ++ttl;
+ }
+ ttl += MULTICAST_TTL_INC;
+ if (ttl < MULTICAST_TTL1) ttl = MULTICAST_TTL1;
+ if (ttl > MULTICAST_TTL_MAX) ttl = MULTICAST_TTL_MAX;
+ return (ttl);
+ } else return(MULTICAST_TTL1);
+}
+
+/*
+ * Calculate the difference between two 32-bit NTP timestamps and return
+ * the result in milliseconds.
+ */
+int
+t_diff(a, b)
+ u_long a, b;
+{
+ int d = a - b;
+
+ return ((d * 125) >> 13);
+}
+
+/*
+ * Swap bytes for poor little-endian machines that don't byte-swap
+ */
+u_long
+byteswap(v)
+ u_long v;
+{
+ return ((v << 24) | ((v & 0xff00) << 8) |
+ ((v >> 8) & 0xff00) | (v >> 24));
+}
+
+#if 0
+/*
+ * XXX incomplete - need private callback data, too?
+ * XXX since dst doesn't get passed through?
+ */
+int
+neighbors_callback(tmo, buf, buflen, igmp, igmplen, addr, addrlen, ts)
+ int tmo;
+ u_char *buf;
+ int buflen;
+ struct igmp *igmp;
+ int igmplen;
+ struct sockaddr *addr;
+ int *addrlen;
+ struct timeval *ts;
+{
+ int len;
+ u_int32 dst;
+ struct ip *ip = (struct ip *)buf;
+
+ if (tmo)
+ return 0;
+
+ if (igmp->igmp_code != DVMRP_NEIGHBORS2)
+ return 0;
+ len = igmplen;
+ /*
+ * Accept DVMRP_NEIGHBORS2 response if it comes from the
+ * address queried or if that address is one of the local
+ * addresses in the response.
+ */
+ if (ip->ip_src.s_addr != dst) {
+ u_int32 *p = (u_int32 *)(igmp + 1);
+ u_int32 *ep = p + (len >> 2);
+ while (p < ep) {
+ u_int32 laddr = *p++;
+ int n = ntohl(*p++) & 0xFF;
+ if (laddr == dst) {
+ ep = p + 1; /* ensure p < ep after loop */
+ break;
+ }
+ p += n;
+ }
+ if (p >= ep)
+ return 0;
+ }
+ return buflen;
+}
+#endif
+
+int
+mtrace_callback(tmo, buf, buflen, igmp, igmplen, addr, addrlen, ts)
+ int tmo;
+ u_char *buf;
+ int buflen;
+ struct igmp *igmp;
+ int igmplen;
+ struct sockaddr *addr;
+ int *addrlen;
+ struct timeval *ts;
+{
+ static u_char *savbuf = NULL;
+ static int savbuflen;
+ static struct sockaddr *savaddr;
+ static int savaddrlen;
+ static struct timeval savts;
+
+ int len = (igmplen - QLEN) / RLEN;
+ struct tr_resp *r = (struct tr_resp *)((struct tr_query *)(igmp + 1) + 1);
+
+ if (tmo == 1) {
+ /*
+ * If we timed out with a packet saved, then return that packet.
+ * send_recv won't send this same packet to the callback again.
+ */
+ if (savbuf) {
+ bcopy(savbuf, buf, savbuflen);
+ free(savbuf);
+ savbuf = NULL;
+ bcopy(savaddr, addr, savaddrlen);
+ free(savaddr);
+ *addrlen = savaddrlen;
+ bcopy(&savts, ts, sizeof(savts));
+ return savbuflen;
+ }
+ return 0;
+ }
+ if (savbuf) {
+ free(savbuf);
+ savbuf = NULL;
+ free(savaddr);
+ }
+ /*
+ * Check for IOS bug described in CSCdi68628, where a router that does
+ * not have multicast enabled responds to an mtrace request with a 1-hop
+ * error packet.
+ * Heuristic is:
+ * If there is only one hop reported in the packet,
+ * And the protocol code is 0,
+ * And there is no previous hop,
+ * And the forwarding information is "Not Forwarding",
+ * And the router is not on the same subnet as the destination of the
+ * trace,
+ * then drop this packet. The "#if 0"'d code saves it and returns
+ * it on timeout, but timeouts are too common (e.g. routers with
+ * limited unicast routing tables, etc).
+ */
+ if (len == 1 && r->tr_rproto == 0 && r->tr_rmtaddr == 0 &&
+ r->tr_rflags == TR_NO_FWD) {
+ u_int32 smask;
+
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_outaddr & smask) != (qdst & smask)) {
+#if 0
+ /* XXX should do this silently? */
+ fprintf(stderr, "mtrace: probably IOS-buggy packet from %s\n",
+ inet_fmt(((struct sockaddr_in *)addr)->sin_addr.s_addr, s1));
+ /* Save the packet to return if a timeout occurs. */
+ savbuf = (u_char *)malloc(buflen);
+ if (savbuf != NULL) {
+ bcopy(buf, savbuf, buflen);
+ savbuflen = buflen;
+ savaddr = (struct sockaddr *)malloc(*addrlen);
+ if (savaddr != NULL) {
+ bcopy(addr, savaddr, *addrlen);
+ savaddrlen = *addrlen;
+ bcopy(ts, &savts, sizeof(savts));
+ } else {
+ free(savbuf);
+ savbuf = NULL;
+ }
+ }
+#endif
+ return 0;
+ }
+ }
+ return buflen;
+}
+
+int
+send_recv(dst, type, code, tries, save, callback)
+ u_int32 dst;
+ int type, code, tries;
+ struct resp_buf *save;
+ callback_t callback;
+{
+ fd_set fds;
+ struct timeval tq, tr, tv;
+ struct ip *ip;
+ struct igmp *igmp;
+ struct tr_query *query, *rquery;
+ struct tr_resp *r;
+ struct sockaddr_in recvaddr;
+ u_int32 local, group;
+ int ipdatalen, iphdrlen, igmpdatalen;
+ int datalen;
+ int count, recvlen, socklen = sizeof(recvaddr);
+ int len;
+ int i;
+
+ if (type == IGMP_MTRACE) {
+ group = qgrp;
+ datalen = sizeof(struct tr_query);
+ } else {
+ group = htonl(0xff03);
+ datalen = 0;
+ }
+ if (IN_MULTICAST(ntohl(dst))) local = lcl_addr;
+ else local = INADDR_ANY;
+
+ /*
+ * If the reply address was not explictly specified, start off
+ * with the standard multicast reply address, or the unicast
+ * address of this host if the unicast flag was specified.
+ * Then, if there is no response after trying half the tries
+ * with multicast, switch to the unicast address of this host
+ * if the multicast flag was not specified. If the TTL was
+ * also not specified, set a multicast TTL and increase it
+ * for every try.
+ */
+ query = (struct tr_query *)(send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ query->tr_raddr = raddr ? raddr : unicast ? lcl_addr : resp_cast;
+ TR_SETTTL(query->tr_rttlqid, rttl ? rttl :
+ IN_MULTICAST(ntohl(query->tr_raddr)) ? get_ttl(save) : UNICAST_TTL);
+ query->tr_src = qsrc;
+ query->tr_dst = qdst;
+
+ for (i = tries ; i > 0; --i) {
+ int oqid;
+
+ if (tries == nqueries && raddr == 0) {
+ if (i == (nqueries >> 1)) {
+ if (multicast && unicast) {
+ query->tr_raddr = resp_cast;
+ if (!rttl)
+ TR_SETTTL(query->tr_rttlqid, get_ttl(save));
+ } else if (!multicast) {
+ query->tr_raddr = lcl_addr;
+ TR_SETTTL(query->tr_rttlqid, UNICAST_TTL);
+ }
+ }
+ if (i < tries && IN_MULTICAST(ntohl(query->tr_raddr)) &&
+ rttl == 0) {
+ TR_SETTTL(query->tr_rttlqid,
+ TR_GETTTL(query->tr_rttlqid) + MULTICAST_TTL_INC);
+ if (TR_GETTTL(query->tr_rttlqid) > MULTICAST_TTL_MAX)
+ TR_SETTTL(query->tr_rttlqid, MULTICAST_TTL_MAX);
+ }
+ }
+
+ /*
+ * Change the qid for each request sent to avoid being confused
+ * by duplicate responses
+ */
+ oqid = TR_GETQID(query->tr_rttlqid);
+ if (staticqid)
+ TR_SETQID(query->tr_rttlqid, staticqid);
+ else
+#ifdef SYSV
+ TR_SETQID(query->tr_rttlqid, ((u_int32)lrand48() >> 8));
+#else
+ TR_SETQID(query->tr_rttlqid, ((u_int32)arc4random() >> 8));
+#endif
+
+ /*
+ * Set timer to calculate delays, then send query
+ */
+ gettimeofday(&tq, 0);
+ send_igmp(local, dst, type, code, group, datalen);
+
+ /*
+ * Wait for response, discarding false alarms
+ */
+ while (TRUE) {
+ if (igmp_socket >= FD_SETSIZE)
+ log(LOG_ERR, 0, "descriptor too big");
+ FD_ZERO(&fds);
+ FD_SET(igmp_socket, &fds);
+ gettimeofday(&tv, 0);
+ tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec;
+ tv.tv_usec = tq.tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec;
+ if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0;
+
+ count = select(igmp_socket + 1, &fds, (fd_set *)0, (fd_set *)0,
+ &tv);
+
+ if (count < 0) {
+ if (errno != EINTR) warn("select");
+ continue;
+ } else if (count == 0) {
+ /*
+ * Timed out. Notify the callback.
+ */
+ if (!callback || (recvlen = (callback)(1, recv_buf, 0, NULL, 0, (struct sockaddr *)&recvaddr, &socklen, &tr)) == 0) {
+ printf("* ");
+ fflush(stdout);
+ break;
+ }
+ } else {
+ /*
+ * Data is available on the socket, so read it.
+ */
+ gettimeofday(&tr, 0);
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, (struct sockaddr *)&recvaddr, &socklen);
+ }
+
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR) warn("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ warnx("packet too short (%u bytes) for IP header", recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0) /* ignore cache creation requests */
+ continue;
+
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len);
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ warnx("packet shorter (%u bytes) than hdr+data len (%u+%u)",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ warnx("IP data field too short (%u bytes) for IGMP from %s",
+ ipdatalen, inet_fmt(ip->ip_src.s_addr, s1));
+ continue;
+ }
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_DVMRP:
+ if (type != IGMP_DVMRP || code != DVMRP_ASK_NEIGHBORS2)
+ continue;
+ if (igmp->igmp_code != DVMRP_NEIGHBORS2) continue;
+ len = igmpdatalen;
+ /*
+ * Accept DVMRP_NEIGHBORS2 response if it comes from the
+ * address queried or if that address is one of the local
+ * addresses in the response.
+ */
+ if (ip->ip_src.s_addr != dst) {
+ u_int32 *p = (u_int32 *)(igmp + 1);
+ u_int32 *ep = p + (len >> 2);
+ while (p < ep) {
+ u_int32 laddr = *p++;
+ int n = ntohl(*p++) & 0xFF;
+ if (laddr == dst) {
+ ep = p + 1; /* ensure p < ep after loop */
+ break;
+ }
+ p += n;
+ }
+ if (p >= ep) continue;
+ }
+ break;
+
+ case IGMP_MTRACE: /* For backward compatibility with 3.3 */
+ case IGMP_MTRACE_RESP:
+ if (type != IGMP_MTRACE) continue;
+ if (igmpdatalen <= QLEN) continue;
+ if ((igmpdatalen - QLEN)%RLEN) {
+ printf("packet with incomplete responses (%d bytes)\n",
+ igmpdatalen);
+ continue;
+ }
+
+ /*
+ * Ignore responses that don't match query.
+ */
+ rquery = (struct tr_query *)(igmp + 1);
+ if (rquery->tr_src != qsrc || rquery->tr_dst != qdst)
+ continue;
+ if (TR_GETQID(rquery->tr_rttlqid) !=
+ TR_GETQID(query->tr_rttlqid)) {
+ if (verbose && TR_GETQID(rquery->tr_rttlqid) == oqid)
+ printf("[D]");
+ continue;
+ }
+ len = (igmpdatalen - QLEN)/RLEN;
+ r = (struct tr_resp *)(rquery+1) + len - 1;
+
+ /*
+ * Ignore trace queries passing through this node when
+ * mtrace is run on an mrouter that is in the path
+ * (needed only because IGMP_MTRACE is accepted above
+ * for backward compatibility with multicast release 3.3).
+ */
+ if (igmp->igmp_type == IGMP_MTRACE) {
+ u_int32 smask;
+
+ VAL_TO_MASK(smask, r->tr_smask);
+ if (len < code && (r->tr_inaddr & smask) != (qsrc & smask)
+ && r->tr_rmtaddr != 0 && !(r->tr_rflags & 0x80))
+ continue;
+ }
+ /*
+ * Some routers will return error messages without
+ * filling in their addresses. We fill in the address
+ * for them.
+ */
+ if (r->tr_outaddr == 0)
+ r->tr_outaddr = recvaddr.sin_addr.s_addr;
+
+ /*
+ * A match, we'll keep this one.
+ */
+ if (len > code) {
+ warnx("num hops received (%d) exceeds request (%d)",
+ len, code);
+ }
+ rquery->tr_raddr = query->tr_raddr; /* Insure these are */
+ TR_SETTTL(rquery->tr_rttlqid, TR_GETTTL(query->tr_rttlqid));
+ /* as we sent them */
+ break;
+
+ default:
+ continue;
+ }
+
+ /*
+ * We're pretty sure we want to use this packet now,
+ * but if the caller gave a callback function, it might
+ * want to handle it instead. Give the callback a chance,
+ * unless the select timed out (in which case the only way
+ * to get here is because the callback returned a packet).
+ */
+ if (callback && (count != 0) && ((callback)(0, recv_buf, recvlen, igmp, igmpdatalen, (struct sockaddr*)&recvaddr, &socklen, &tr)) == 0) {
+ /*
+ * The callback function didn't like this packet.
+ * Go try receiving another one.
+ */
+ continue;
+ }
+
+ /*
+ * Most of the sanity checking done at this point.
+ * Return this packet we have been waiting for.
+ */
+ if (save) {
+ save->qtime = ((tq.tv_sec + JAN_1970) << 16) +
+ (tq.tv_usec << 10) / 15625;
+ save->rtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ save->len = len;
+ bcopy((char *)igmp, (char *)&save->igmp, ipdatalen);
+ }
+ return (recvlen);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Most of this code is duplicated elsewhere. I'm not sure if
+ * the duplication is absolutely required or not.
+ *
+ * Ideally, this would keep track of ongoing statistics
+ * collection and print out statistics. (& keep track
+ * of h-b-h traces and only print the longest) For now,
+ * it just snoops on what traces it can.
+ */
+void
+passive_mode()
+{
+ struct timeval tr;
+ time_t tr_sec;
+ struct ip *ip;
+ struct igmp *igmp;
+ struct tr_resp *r;
+ struct sockaddr_in recvaddr;
+ struct tm *now;
+ char timebuf[32];
+ int socklen;
+ int ipdatalen, iphdrlen, igmpdatalen;
+ int len, recvlen;
+ int qid;
+ u_int32 smask;
+ struct mtrace *remembered = NULL, *m, *n, **nn;
+ int pc = 0;
+
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr);
+ } else k_join(htonl(0xE0000120), lcl_addr);
+
+ while (1) {
+ fflush(stdout); /* make sure previous trace is flushed */
+
+ socklen = sizeof(recvaddr);
+ recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
+ 0, (struct sockaddr *)&recvaddr, &socklen);
+ gettimeofday(&tr,0);
+
+ if (recvlen <= 0) {
+ if (recvlen && errno != EINTR) warn("recvfrom");
+ continue;
+ }
+
+ if (recvlen < sizeof(struct ip)) {
+ warnx("packet too short (%u bytes) for IP header", recvlen);
+ continue;
+ }
+ ip = (struct ip *) recv_buf;
+ if (ip->ip_p == 0) /* ignore cache creation requests */
+ continue;
+
+ iphdrlen = ip->ip_hl << 2;
+#ifdef RAW_INPUT_IS_RAW
+ ipdatalen = ntohs(ip->ip_len);
+#else
+ ipdatalen = ip->ip_len;
+#endif
+ if (iphdrlen + ipdatalen != recvlen) {
+ warnx("packet shorter (%u bytes) than hdr+data len (%u+%u)",
+ recvlen, iphdrlen, ipdatalen);
+ continue;
+ }
+
+ igmp = (struct igmp *) (recv_buf + iphdrlen);
+ igmpdatalen = ipdatalen - IGMP_MINLEN;
+ if (igmpdatalen < 0) {
+ warnx("IP data field too short (%u bytes) for IGMP from %s",
+ ipdatalen, inet_fmt(ip->ip_src.s_addr, s1));
+ continue;
+ }
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_MTRACE: /* For backward compatibility with 3.3 */
+ case IGMP_MTRACE_RESP:
+ if (igmpdatalen < QLEN) continue;
+ if ((igmpdatalen - QLEN)%RLEN) {
+ printf("packet with incorrect datalen\n");
+ continue;
+ }
+
+ len = (igmpdatalen - QLEN)/RLEN;
+
+ break;
+
+ default:
+ continue;
+ }
+
+ base.qtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ base.rtime = ((tr.tv_sec + JAN_1970) << 16) +
+ (tr.tv_usec << 10) / 15625;
+ base.len = len;
+ bcopy((char *)igmp, (char *)&base.igmp, ipdatalen);
+ /*
+ * If the user specified which traces to monitor,
+ * only accept traces that correspond to the
+ * request
+ */
+ if ((qsrc != 0 && qsrc != base.qhdr.tr_src) ||
+ (qdst != 0 && qdst != base.qhdr.tr_dst) ||
+ (qgrp != 0 && qgrp != igmp->igmp_group.s_addr))
+ continue;
+
+ /* XXX This should be a hash table */
+ /* XXX garbage-collection should be more efficient */
+ for (nn = &remembered, n = *nn, m = 0; n; n = *nn) {
+ if ((n->base.qhdr.tr_src == base.qhdr.tr_src) &&
+ (n->base.qhdr.tr_dst == base.qhdr.tr_dst) &&
+ (n->base.igmp.igmp_group.s_addr == igmp->igmp_group.s_addr)) {
+ m = n;
+ m->last = tr;
+ }
+ if (tr.tv_sec - n->last.tv_sec > 500) { /* XXX don't hardcode */
+ *nn = n->next;
+ free(n);
+ } else {
+ nn = &n->next;
+ }
+ }
+
+ tr_sec = tr.tv_sec;
+ now = localtime(&tr_sec);
+ strftime(timebuf, sizeof(timebuf), "%b %e %k:%M:%S", now);
+ printf("Mtrace %s at %s",
+ len == 0 ? "query" :
+ igmp->igmp_type == IGMP_MTRACE_RESP ? "response" :
+ "in transit",
+ timebuf);
+ if (len == 0)
+ printf(" by %s", inet_fmt(recvaddr.sin_addr.s_addr, s1));
+ if (!IN_MULTICAST(base.qhdr.tr_raddr))
+ printf(", resp to %s", (len == 0 && recvaddr.sin_addr.s_addr == base.qhdr.tr_raddr) ? "same" : inet_fmt(base.qhdr.tr_raddr, s1));
+ else
+ printf(", respttl %d", TR_GETTTL(base.qhdr.tr_rttlqid));
+ printf(", qid %06x\n", qid = TR_GETQID(base.qhdr.tr_rttlqid));
+ printf("packet from %s to %s\n",
+ inet_fmt(ip->ip_src.s_addr, s1),
+ inet_fmt(ip->ip_dst.s_addr, s2));
+
+ printf("from %s to %s via group %s (mxhop=%d)\n",
+ inet_fmt(base.qhdr.tr_dst, s1), inet_fmt(base.qhdr.tr_src, s2),
+ inet_fmt(igmp->igmp_group.s_addr, s3), igmp->igmp_code);
+ if (len == 0) {
+ printf("\n");
+ continue;
+ }
+ r = base.resps + base.len - 1;
+ /*
+ * Some routers will return error messages without
+ * filling in their addresses. We fill in the address
+ * for them.
+ */
+ if (r->tr_outaddr == 0)
+ r->tr_outaddr = recvaddr.sin_addr.s_addr;
+
+ /*
+ * If there was a previous trace, it see if this is a
+ * statistics candidate.
+ */
+ if (m && base.len == m->base.len &&
+ !(pc = path_changed(&m->base, &base))) {
+ /*
+ * Some mtrace responders send multiple copies of the same
+ * reply. Skip this packet if it's got the same query-id
+ * as the last one.
+ */
+ if (m->lastqid == qid) {
+ printf("Skipping duplicate reply\n");
+ continue;
+ }
+
+ m->lastqid = qid;
+
+ ++m->nresp;
+
+ bcopy(&base, m->new, sizeof(base));
+
+ printf("Results after %d seconds:\n\n",
+ (int)((m->new->qtime - m->base.qtime) >> 16));
+ fixup_stats(&m->base, m->prev, m->new, m->bugs);
+ print_stats(&m->base, m->prev, m->new, m->bugs, m->names);
+ m->prev = m->new;
+ m->new = &m->incr[(m->nresp & 1)];
+
+ continue;
+ }
+
+ if (m == NULL) {
+ m = (struct mtrace *)malloc(sizeof(struct mtrace));
+ if (m == NULL) {
+ fprintf(stderr, "Out of memory!\n");
+ continue;
+ }
+ bzero(m, sizeof(struct mtrace));
+ m->next = remembered;
+ remembered = m;
+ bcopy(&tr, &m->last, sizeof(tr));
+ }
+
+ /* Either it's a hop-by-hop in progress, or the path changed. */
+ if (pc) {
+ printf("[Path Changed...]\n");
+ bzero(m->bugs, sizeof(m->bugs));
+ }
+ bcopy(&base, &m->base, sizeof(base));
+ m->prev = &m->base;
+ m->new = &m->incr[0];
+ m->nresp = 0;
+
+ printf(" 0 ");
+ print_host(base.qhdr.tr_dst);
+ printf("\n");
+ print_trace(1, &base, m->names);
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_inaddr & smask) == (base.qhdr.tr_src & smask)) {
+ printf("%3d ", -(base.len+1));
+ print_host(base.qhdr.tr_src);
+ printf("\n");
+ } else if (r->tr_rmtaddr != 0) {
+ printf("%3d ", -(base.len+1));
+ print_host(r->tr_rmtaddr);
+ printf(" %s\n", r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
+ }
+ printf("\n");
+ }
+}
+
+char *
+print_host(addr)
+ u_int32 addr;
+{
+ return print_host2(addr, 0);
+}
+
+/*
+ * On some routers, one interface has a name and the other doesn't.
+ * We always print the address of the outgoing interface, but can
+ * sometimes get the name from the incoming interface. This might be
+ * confusing but should be slightly more helpful than just a "?".
+ */
+char *
+print_host2(addr1, addr2)
+ u_int32 addr1, addr2;
+{
+ char *name;
+
+ if (numeric) {
+ printf("%s", inet_fmt(addr1, s1));
+ return ("");
+ }
+ name = inet_name(addr1);
+ if (*name == '?' && *(name + 1) == '\0' && addr2 != 0)
+ name = inet_name(addr2);
+ printf("%s (%s)", name, inet_fmt(addr1, s1));
+ return (name);
+}
+
+/*
+ * Print responses as received (reverse path from dst to src)
+ */
+void
+print_trace(idx, buf, names)
+ int idx;
+ struct resp_buf *buf;
+ char **names;
+{
+ struct tr_resp *r;
+ char *name;
+ int i;
+ int hop;
+ char *ms;
+
+ i = abs(idx);
+ r = buf->resps + i - 1;
+
+ for (; i <= buf->len; ++i, ++r) {
+ if (idx > 0) printf("%3d ", -i);
+ name = print_host2(r->tr_outaddr, r->tr_inaddr);
+ if (r->tr_rflags != TR_NO_RTE)
+ printf(" %s thresh^ %d", proto_type(r->tr_rproto), r->tr_fttl);
+ if (verbose) {
+ hop = t_diff(ntohl(r->tr_qarr), buf->qtime);
+ ms = scale(&hop);
+ printf(" %d%s", hop, ms);
+ }
+ printf(" %s", flag_type(r->tr_rflags));
+ if (i > 1 && r->tr_outaddr != (r-1)->tr_rmtaddr) {
+ printf(" !RPF!");
+ print_host((r-1)->tr_rmtaddr);
+ }
+ if (r->tr_rflags != TR_NO_RTE) {
+ if (r->tr_smask <= 1) /* MASK_TO_VAL() returns 1 for default */
+ printf(" [default]");
+ else if (verbose) {
+ u_int32 smask;
+
+ VAL_TO_MASK(smask, r->tr_smask);
+ printf(" [%s]", inet_fmts(buf->qhdr.tr_src & smask,
+ smask, s1));
+ }
+ }
+ printf("\n");
+ if (names[i-1])
+ free(names[i-1]);
+ names[i-1]=malloc(strlen(name) + 1);
+ strcpy(names[i-1], name);
+ }
+}
+
+/*
+ * See what kind of router is the next hop
+ */
+int
+what_kind(buf, why)
+ struct resp_buf *buf;
+ char *why;
+{
+ u_int32 smask;
+ int retval;
+ int hops = buf->len;
+ struct tr_resp *r = buf->resps + hops - 1;
+ u_int32 next = r->tr_rmtaddr;
+
+ retval = send_recv(next, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0], NULL);
+ print_host(next);
+ if (retval) {
+ u_int32 version = ntohl(incr[0].igmp.igmp_group.s_addr);
+ u_int32 *p = (u_int32 *)incr[0].ndata;
+ u_int32 *ep = p + (incr[0].len >> 2);
+ char *type = "version ";
+
+ retval = 0;
+ switch (version & 0xFF) {
+ case 1:
+ type = "proteon/mrouted ";
+ retval = 1;
+ break;
+
+ case 10:
+ case 11:
+ type = "cisco ";
+ }
+ printf(" [%s%d.%d] %s\n",
+ type, version & 0xFF, (version >> 8) & 0xFF,
+ why);
+ VAL_TO_MASK(smask, r->tr_smask);
+ while (p < ep) {
+ u_int32 laddr = *p++;
+ int flags = (ntohl(*p) & 0xFF00) >> 8;
+ int n = ntohl(*p++) & 0xFF;
+ if (!(flags & (DVMRP_NF_DOWN | DVMRP_NF_DISABLED)) &&
+ (laddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(hops+2));
+ print_host(qsrc);
+ printf("\n");
+ return 1;
+ }
+ p += n;
+ }
+ return retval;
+ }
+ printf(" %s\n", why);
+ return 0;
+}
+
+
+char *
+scale(hop)
+ int *hop;
+{
+ if (*hop > -1000 && *hop < 10000) return (" ms");
+ *hop /= 1000;
+ if (*hop > -1000 && *hop < 10000) return (" s ");
+ return ("s ");
+}
+
+/*
+ * Calculate and print one line of packet loss and packet rate statistics.
+ * Checks for count of all ones from mrouted 2.3 that doesn't have counters.
+ */
+#define NEITHER 0
+#define INS 1
+#define OUTS 2
+#define BOTH 3
+void
+stat_line(r, s, have_next, rst)
+ struct tr_resp *r, *s;
+ int have_next;
+ int *rst;
+{
+ int timediff = (ntohl(s->tr_qarr) - ntohl(r->tr_qarr)) >> 16;
+ int v_lost, v_pct;
+ int g_lost, g_pct;
+ int v_out = ntohl(s->tr_vifout) - ntohl(r->tr_vifout);
+ int g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
+ int v_pps, g_pps;
+ char v_str[8], g_str[8];
+ int vhave = NEITHER;
+ int ghave = NEITHER;
+ int gmissing = NEITHER;
+ char whochar;
+ int badtime = 0;
+
+ if (timediff == 0) {
+ badtime = 1;
+ /* Might be 32 bits of int seconds instead of 16int+16frac */
+ timediff = ntohl(s->tr_qarr) - ntohl(r->tr_qarr);
+ if (timediff == 0 || abs(timediff - statint) > statint)
+ timediff = 1;
+ }
+ v_pps = v_out / timediff;
+ g_pps = g_out / timediff;
+
+#define STATS_MISSING(x) ((x) == 0xFFFFFFFF)
+
+ if (!STATS_MISSING(s->tr_vifout) && !STATS_MISSING(r->tr_vifout))
+ vhave |= OUTS;
+ if (STATS_MISSING(s->tr_pktcnt) || STATS_MISSING(r->tr_pktcnt))
+ gmissing |= OUTS;
+ if (!(*rst & BUG_NOPRINT))
+ ghave |= OUTS;
+
+ if (have_next) {
+ --r, --s, --rst;
+ if (!STATS_MISSING(s->tr_vifin) && !STATS_MISSING(r->tr_vifin))
+ vhave |= INS;
+ if (STATS_MISSING(s->tr_pktcnt) || STATS_MISSING(r->tr_pktcnt))
+ gmissing |= INS;
+ if (!(*rst & BUG_NOPRINT))
+ ghave |= INS;
+ }
+
+ /*
+ * Stats can be missing for any number of reasons:
+ * - The hop may not be capable of collecting stats
+ * - Traffic may be getting dropped at the previous hop
+ * and so this hop may not have any state
+ *
+ * We need a stronger heuristic to tell between these
+ * two cases; in case 1 we don't want to print the stats
+ * and in case 2 we want to print 100% loss. We used to
+ * err on the side of not printing, which is less useful
+ * than printing 100% loss and dealing with it.
+ */
+#if 0
+ /*
+ * If both hops report as missing, then it's likely that there's just
+ * no traffic flowing.
+ *
+ * If just one hop is missing, then we really don't have it.
+ */
+ if (gmissing != BOTH)
+ ghave &= ~gmissing;
+#endif
+
+ whochar = have_next ? '^' : ' ';
+ switch (vhave) {
+ case BOTH:
+ v_lost = v_out - (ntohl(s->tr_vifin) - ntohl(r->tr_vifin));
+ if (v_out) v_pct = v_lost * 100 / v_out;
+ else v_pct = 0;
+ if (-20 < v_pct && v_pct < 101 && v_out > 10)
+ sprintf(v_str, "%3d%%", v_pct);
+ else if (v_pct < -900 && v_out > 10)
+ sprintf(v_str, "%3dx", (int)(-v_pct / 100. + 1.));
+ else if (v_pct <= -20 && v_out > 10)
+ sprintf(v_str, "%1.1fx", -v_pct / 100. + 1.);
+ else
+ memcpy(v_str, " -- ", 5);
+
+ if (tunstats)
+ printf("%6d/%-5d=%s", v_lost, v_out, v_str);
+ else
+ printf(" ");
+ printf("%4d pps", v_pps);
+ if (v_pps && badtime)
+ printf("?");
+
+ break;
+
+ case INS:
+ v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin);
+ v_pps = v_out / timediff;
+ whochar = 'v';
+ /* FALLTHROUGH */
+
+ case OUTS:
+ if (tunstats)
+ printf(" %c%-5d ", whochar, v_out);
+ else
+ printf(" %c", whochar);
+ printf("%4d pps", v_pps);
+ if (v_pps && badtime)
+ printf("?");
+
+ break;
+
+ case NEITHER:
+ if (ghave != NEITHER)
+ if (tunstats)
+ printf(" ");
+ else
+ printf(" ");
+
+ break;
+ }
+
+ whochar = have_next ? '^' : ' ';
+ switch (ghave) {
+ case BOTH:
+ g_lost = g_out - (ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
+ if (g_out) g_pct = g_lost * 100 / g_out;
+ else g_pct = 0;
+ if (-20 < g_pct && g_pct < 101 && g_out > 10)
+ sprintf(g_str, "%3d%%", g_pct);
+ else if (g_pct < -900 && g_out > 10)
+ sprintf(g_str, "%3dx", (int)(-g_pct / 100. + 1.));
+ else if (g_pct <= -20 && g_out > 10)
+ sprintf(g_str, "%1.1fx", -g_pct / 100. + 1.);
+ else
+ memcpy(g_str, " -- ", 5);
+
+ printf("%s%6d/%-5d=%s%4d pps",
+ tunstats ? "" : " ", g_lost, g_out, g_str, g_pps);
+ if (g_pps && badtime)
+ printf("?");
+ printf("\n");
+ break;
+
+#if 0
+ case INS:
+ g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
+ g_pps = g_out / timediff;
+ whochar = 'v';
+ /* FALLTHROUGH */
+#endif
+
+ case OUTS:
+ printf("%s ?/%-5d %4d pps",
+ tunstats ? "" : " ", g_out, g_pps);
+ if (badtime)
+ printf("?");
+ printf("\n");
+ break;
+
+ case INS:
+ case NEITHER:
+ printf("\n");
+ break;
+ }
+
+
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(s->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(s->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(s->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(r->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(r->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(r->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",
+ (long)(ntohl(s->tr_vifin) - ntohl(r->tr_vifin)));
+ printf("v_out: %ld ",
+ (long)(ntohl(s->tr_vifout) - ntohl(r->tr_vifout)));
+ printf("pkts: %ld ", (long)(ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt)));
+ printf("time: %d\n", timediff);
+ printf("\t\t\t\treset: %x hoptime: %lx\n", *rst, ntohl(s->tr_qarr));
+ }
+}
+
+/*
+ * A fixup to check if any pktcnt has been reset, and to fix the
+ * byteorder bugs in mrouted 3.6 on little-endian machines.
+ *
+ * XXX Since periodic traffic sources are likely to have their
+ * pktcnt periodically reset, should we save old values when
+ * the reset occurs to keep slightly better statistics over
+ * the long term? (e.g. SAP)
+ */
+void
+fixup_stats(base, prev, new, bugs)
+ struct resp_buf *base, *prev, *new;
+ int *bugs;
+{
+ int rno = base->len;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = bugs + rno;
+ int res;
+ int cleanup = 0;
+
+ /* Check for byte-swappers. Only check on the first trace,
+ * since long-running traces can wrap around and falsely trigger. */
+ while (--rno >= 0) {
+#ifdef TEST_ONLY
+ u_int32 nvifout = ntohl(n->tr_vifout);
+ u_int32 pvifout = ntohl(p->tr_vifout);
+#endif
+ --n; --p; --b;
+#ifdef TEST_ONLY /*XXX this is still buggy, so disable it for release */
+ if ((*r & BUG_SWAP) ||
+ ((base == prev) &&
+ (nvifout - pvifout) > (byteswap(nvifout) - byteswap(pvifout)))) {
+ if (1 || debug > 2) {
+ printf("ip %s swaps; b %08x p %08x n %08x\n",
+ inet_fmt(n->tr_inaddr, s1),
+ ntohl(b->tr_vifout), pvifout, nvifout);
+ }
+ /* This host sends byteswapped reports; swap 'em */
+ if (!(*r & BUG_SWAP)) {
+ *r |= BUG_SWAP;
+ b->tr_qarr = byteswap(b->tr_qarr);
+ b->tr_vifin = byteswap(b->tr_vifin);
+ b->tr_vifout = byteswap(b->tr_vifout);
+ b->tr_pktcnt = byteswap(b->tr_pktcnt);
+ }
+
+ n->tr_qarr = byteswap(n->tr_qarr);
+ n->tr_vifin = byteswap(n->tr_vifin);
+ n->tr_vifout = byteswap(n->tr_vifout);
+ n->tr_pktcnt = byteswap(n->tr_pktcnt);
+ }
+#endif
+ /*
+ * A missing parenthesis in mrouted 3.5-3.8's prune.c
+ * causes extremely bogus time diff's.
+ * One half of the time calculation was
+ * inside an htonl() and one half wasn't. Therefore, on
+ * a little-endian machine, both halves of the calculation
+ * would get added together in the little end. Thus, the
+ * low-order 2 bytes are either 0000 (no overflow) or
+ * 0100 (overflow from the addition).
+ *
+ * Odds are against these particular bit patterns
+ * happening in both prev and new for actual time values.
+ */
+ if ((*r & BUG_BOGUSTIME) || (((ntohl(n->tr_qarr) & 0xfeff) == 0x0000) &&
+ ((ntohl(p->tr_qarr) & 0xfeff) == 0x0000))) {
+ *r |= BUG_BOGUSTIME;
+ n->tr_qarr = new->rtime;
+ p->tr_qarr = prev->rtime;
+ b->tr_qarr = base->rtime;
+ }
+ }
+
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+ n = new->resps + rno;
+ r = bugs + rno;
+
+ while (--rno >= 0) {
+ --n; --p; --b; --r;
+ /*
+ * This hop has reset if:
+ * - There were statistics in the base AND previous pass, AND
+ * - There are less packets this time than the first time and
+ * we didn't reset last time, OR
+ * - There are less packets this time than last time, OR
+ * - There are no statistics on this pass.
+ *
+ * The "and we didn't reset last time" is necessary in the
+ * first branch of the OR because if the base is large and
+ * we reset last time but the constant-resetter-avoidance
+ * code kicked in so we delayed the copy of prev to base,
+ * new could still be below base so we trigger the
+ * constant-resetter code even though it was really only
+ * a single reset.
+ */
+ res = ((b->tr_pktcnt != 0xFFFFFFFF) && (p->tr_pktcnt != 0xFFFFFFFF) &&
+ ((!(*r & BUG_RESET) && ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) ||
+ (ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt)) ||
+ (n->tr_pktcnt == 0xFFFFFFFF)));
+ if (debug > 2) {
+ printf("\t\tip=%s, r=%d, res=%d\n", inet_fmt(b->tr_inaddr, s1), *r, res);
+ if (res)
+ printf("\t\tbase=%ld, prev=%ld, new=%ld\n", ntohl(b->tr_pktcnt),
+ ntohl(p->tr_pktcnt), ntohl(n->tr_pktcnt));
+ }
+ if (*r & BUG_RESET) {
+ if (res || (*r & BUG_RESET2X)) {
+ /*
+ * This router appears to be a 3.4 with that nasty ol'
+ * neighbor version bug, which causes it to constantly
+ * reset. Just nuke the statistics for this node, and
+ * don't even bother giving it the benefit of the
+ * doubt from now on.
+ */
+ p->tr_pktcnt = b->tr_pktcnt = n->tr_pktcnt;
+ *r |= BUG_RESET2X;
+ } else {
+ /*
+ * This is simply the situation that the original
+ * fixup_stats was meant to deal with -- that a
+ * 3.3 or 3.4 router deleted a cache entry while
+ * traffic was still active.
+ */
+ *r &= ~BUG_RESET;
+ cleanup = 1;
+ }
+ } else
+ if (res)
+ *r |= BUG_RESET;
+ }
+
+ if (cleanup == 0) return;
+
+ /*
+ * If some hop reset its counters and didn't continue to
+ * reset, then we pretend that the previous
+ * trace was the first one.
+ */
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+
+ while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt;
+ base->qtime = prev->qtime;
+ base->rtime = prev->rtime;
+}
+
+/*
+ * Check per-source losses along path and compare with threshold.
+ */
+int
+check_thresh(thresh, base, prev, new)
+ int thresh;
+ struct resp_buf *base, *prev, *new;
+{
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int g_out, g_lost;
+
+ while (TRUE) {
+ if ((n->tr_inaddr != b->tr_inaddr) ||
+ (n->tr_outaddr != b->tr_outaddr) ||
+ (n->tr_rmtaddr != b->tr_rmtaddr))
+ return 1; /* Route changed */
+
+ if (rno-- < 1) break;
+ g_out = ntohl(n->tr_pktcnt) - ntohl(p->tr_pktcnt);
+ b--; n--; p--;
+ g_lost = g_out - (ntohl(n->tr_pktcnt) - ntohl(p->tr_pktcnt));
+ if (g_out && ((g_lost * 100 + (g_out >> 1))/ g_out) > thresh) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Print responses with statistics for forward path (from src to dst)
+ */
+int
+print_stats(base, prev, new, bugs, names)
+ struct resp_buf *base, *prev, *new;
+ int *bugs;
+ char **names;
+{
+ int rtt, hop;
+ char *ms;
+ u_int32 smask;
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *p = prev->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+ int *r = bugs + rno;
+ u_long resptime = new->rtime;
+ u_long qarrtime = ntohl(n->tr_qarr);
+ u_int ttl = MaX(1, n->tr_fttl) + 1;
+ int first = (base == prev);
+
+ VAL_TO_MASK(smask, b->tr_smask);
+ printf(" Source Response Dest ");
+ if (tunstats)
+ printf("Packet Statistics For Only For Traffic\n");
+ else
+ printf("Overall Packet Statistics For Traffic From\n");
+ (void)inet_fmt(base->qhdr.tr_src, s1);
+ printf("%-15s %-15s ",
+ ((b->tr_inaddr & smask) == (base->qhdr.tr_src & smask)) ?
+ s1 : " * * * ",
+ inet_fmt(base->qhdr.tr_raddr, s2));
+ (void)inet_fmt(base->igmp.igmp_group.s_addr, s2);
+ if (tunstats)
+ printf("All Multicast Traffic From %s\n", s1);
+ else
+ printf("Packet %s To %s\n", s1, s2);
+ rtt = t_diff(resptime, new->qtime);
+ ms = scale(&rtt);
+ printf(" %c __/ rtt%5d%s ",
+ (first && !verbose) ? 'v' : '|', rtt, ms);
+ if (tunstats)
+ printf("Lost/Sent = Pct Rate To %s\n", s2);
+ else
+ printf(" Rate Lost/Sent = Pct Rate\n");
+ if (!first || verbose) {
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v / hop%5d%s ", hop, ms);
+ if (tunstats)
+ printf("--------------------- --------------------\n");
+ else
+ printf("------- ---------------------\n");
+ }
+ if ((b->tr_inaddr & smask) != (base->qhdr.tr_src & smask) &&
+ b->tr_rmtaddr != 0) {
+ printf("%-15s %-14s is the previous hop\n", inet_fmt(b->tr_rmtaddr, s1),
+ inet_name(b->tr_rmtaddr));
+ printf(" v ^\n");
+ }
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(n->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(n->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(n->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", (long)ntohl(b->tr_vifin));
+ printf("v_out: %ld ", (long)ntohl(b->tr_vifout));
+ printf("pkts: %ld\n", (long)ntohl(b->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",
+ (long)(ntohl(n->tr_vifin) - ntohl(b->tr_vifin)));
+ printf("v_out: %ld ",
+ (long)(ntohl(n->tr_vifout) - ntohl(b->tr_vifout)));
+ printf("pkts: %ld\n",
+ (long)(ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt)));
+ printf("\t\t\t\treset: %x hoptime: %lx\n", *r, (long)ntohl(n->tr_qarr));
+ }
+
+ while (TRUE) {
+ if ((n->tr_inaddr != b->tr_inaddr) ||
+ (n->tr_outaddr != b->tr_outaddr) ||
+ (n->tr_rmtaddr != b->tr_rmtaddr))
+ return 1; /* Route changed */
+
+ if ((n->tr_inaddr != n->tr_outaddr) && n->tr_inaddr)
+ printf("%-15s\n", inet_fmt(n->tr_inaddr, s1));
+ printf("%-15s %-14s %s%s\n", inet_fmt(n->tr_outaddr, s1), names[rno],
+ flag_type(n->tr_rflags),
+ (*r & BUG_NOPRINT) ? " [reset counters]" : "");
+
+ if (rno-- < 1) break;
+
+ printf(" %c ^ ttl%5d ", (first && !verbose) ? 'v' : '|',
+ ttl);
+ stat_line(p, n, TRUE, r);
+ if (!first || verbose) {
+ resptime = qarrtime;
+ qarrtime = ntohl((n-1)->tr_qarr);
+ hop = t_diff(resptime, qarrtime);
+ ms = scale(&hop);
+ printf(" v | hop%5d%s", hop, ms);
+ if (first)
+ printf("\n");
+ else
+ stat_line(b, n, TRUE, r);
+ }
+
+ --b, --p, --n, --r;
+ ttl = MaX(ttl, MaX(1, n->tr_fttl) + base->len - rno);
+ }
+
+ printf(" %c \\__ ttl%5d ", (first && !verbose) ? 'v' : '|',
+ ttl);
+ stat_line(p, n, FALSE, r);
+ if (!first || verbose) {
+ hop = t_diff(qarrtime, new->qtime);
+ ms = scale(&hop);
+ printf(" v \\ hop%5d%s", hop, ms);
+ if (first)
+ printf("\n");
+ else
+ stat_line(b, n, FALSE, r);
+ }
+ printf("%-15s %s\n", inet_fmt(base->qhdr.tr_dst, s1),
+ !passive ? inet_fmt(lcl_addr, s2) : " * * * ");
+ printf(" Receiver Query Source\n\n");
+ return 0;
+}
+
+/*
+ * Determine whether or not the path has changed.
+ */
+int
+path_changed(base, new)
+ struct resp_buf *base, *new;
+{
+ int rno = base->len - 1;
+ struct tr_resp *b = base->resps + rno;
+ struct tr_resp *n = new->resps + rno;
+
+ while (rno-- >= 0) {
+ if ((n->tr_inaddr != b->tr_inaddr) ||
+ (n->tr_outaddr != b->tr_outaddr) ||
+ (n->tr_rmtaddr != b->tr_rmtaddr))
+ return 1; /* Route changed */
+ if ((b->tr_rflags == TR_NO_RTE) &&
+ (n->tr_rflags != TR_NO_RTE))
+ return 1; /* Route got longer? */
+ --n;
+ --b;
+ }
+ return 0;
+}
+
+
+/***************************************************************************
+ * main
+ ***************************************************************************/
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int udp;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+ int recvlen;
+ struct timeval tv;
+ struct resp_buf *prev, *new;
+ struct tr_resp *r;
+ u_int32 smask;
+ int rno;
+ int hops, nexthop, tries;
+ u_int32 lastout = 0;
+ int numstats = 1;
+ int waittime;
+ int seed;
+ int hopbyhop;
+ int i;
+ int printed = 1;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ /*
+ * We might get spawned by vat with the audio device open.
+ * Close everything but stdin, stdout, stderr.
+ */
+ for (i = 3; i < 255; i++)
+ close(i);
+
+ init_igmp();
+ setuid(getuid());
+
+ argv++, argc--;
+ if (argc == 0) usage();
+
+ while (argc > 0 && *argv[0] == '-') {
+ char *p = *argv++; argc--;
+ p++;
+ do {
+ char c = *p++;
+ char *arg = (char *) 0;
+ if (isdigit(*p)) {
+ arg = p;
+ p = "";
+ } else if (argc > 0) arg = argv[0];
+ switch (c) {
+ case 'd': /* Unlisted debug print option */
+ if (arg && isdigit(*arg)) {
+ debug = atoi(arg);
+ if (debug < 0) debug = 0;
+ if (debug > 3) debug = 3;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'M': /* Use multicast for reponse */
+ multicast = TRUE;
+ break;
+ case 'U': /* Use unicast for response */
+ unicast = TRUE;
+ break;
+ case 'L': /* Trace w/ loss threshold */
+ if (arg && isdigit(*arg)) {
+ lossthresh = atoi(arg);
+ if (lossthresh < 0)
+ lossthresh = 0;
+ numstats = 3153600;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ break;
+ case 'O': /* Don't use IP options */
+ sendopts = FALSE;
+ break;
+ case 'P': /* Just watch the path */
+ printstats = FALSE;
+ numstats = 3153600;
+ break;
+ case 'Q': /* (undoc.) always use this QID */
+ if (arg && isdigit(*arg)) {
+ staticqid = atoi(arg);
+ if (staticqid < 0)
+ staticqid = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ break;
+ case 'T': /* Print confusing tunnel stats */
+ tunstats = TRUE;
+ break;
+ case 'W': /* Cisco's "weak" mtrace */
+ weak = TRUE;
+ break;
+ case 'V': /* Print version and exit */
+ /*
+ * FreeBSD wants to have its own Id string, so
+ * determination of the version number has to change.
+ * XXX Note that this must be changed by hand on importing
+ * XXX new versions!
+ */
+ {
+ char *r = strdup(rcsid);
+ char *s = strchr(r, ',');
+
+ while (s && *(s+1) != 'v')
+ s = strchr(s + 1, ',');
+
+ if (s) {
+ char *q;
+
+ s += 3; /* , v sp */
+ q = strchr(s, ' ');
+ if (q)
+ *q = '\0';
+ fprintf(stderr, "mtrace version 5.2/%s\n", s);
+ } else {
+ fprintf(stderr, "mtrace could not determine version number!?\n");
+ }
+ exit(1);
+ }
+ break;
+ case 'l': /* Loop updating stats indefinitely */
+ numstats = 3153600;
+ break;
+ case 'n': /* Don't reverse map host addresses */
+ numeric = TRUE;
+ break;
+ case 'p': /* Passive listen for traces */
+ passive = TRUE;
+ break;
+ case 'v': /* Verbosity */
+ verbose = TRUE;
+ break;
+ case 's': /* Short form, don't wait for stats */
+ numstats = 0;
+ break;
+ case 'w': /* Time to wait for packet arrival */
+ if (arg && isdigit(*arg)) {
+ timeout = atoi(arg);
+ if (timeout < 1) timeout = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'f': /* first hop */
+ if (arg && isdigit(*arg)) {
+ qno = atoi(arg);
+ if (qno > MAXHOPS) qno = MAXHOPS;
+ else if (qno < 1) qno = 0;
+ if (arg == argv[0]) argv++, argc--;
+ fflag++;
+ break;
+ } else
+ usage();
+ case 'm': /* Max number of hops to trace */
+ if (arg && isdigit(*arg)) {
+ qno = atoi(arg);
+ if (qno > MAXHOPS) qno = MAXHOPS;
+ else if (qno < 1) qno = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'q': /* Number of query retries */
+ if (arg && isdigit(*arg)) {
+ nqueries = atoi(arg);
+ if (nqueries < 1) nqueries = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'g': /* Last-hop gateway (dest of query) */
+ if (arg && (gwy = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 't': /* TTL for query packet */
+ if (arg && isdigit(*arg)) {
+ qttl = atoi(arg);
+ if (qttl < 1) qttl = 1;
+ rttl = qttl;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'e': /* Extra hops past non-responder */
+ if (arg && isdigit(*arg)) {
+ extrahops = atoi(arg);
+ if (extrahops < 0) extrahops = 0;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'r': /* Dest for response packet */
+ if (arg && (raddr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'i': /* Local interface address */
+ if (arg && (lcl_addr = host_addr(arg))) {
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ case 'S': /* Stat accumulation interval */
+ if (arg && isdigit(*arg)) {
+ statint = atoi(arg);
+ if (statint < 1) statint = 1;
+ if (arg == argv[0]) argv++, argc--;
+ break;
+ } else
+ usage();
+ default:
+ usage();
+ }
+ } while (*p);
+ }
+
+ if (argc > 0 && (qsrc = host_addr(argv[0]))) { /* Source of path */
+ if (IN_MULTICAST(ntohl(qsrc))) {
+ if (gwy) {
+ /* Should probably rewrite arg parsing at some point, as
+ * this makes "mtrace -g foo 224.1.2.3 224.2.3.4" valid!... */
+ qgrp = qsrc;
+ qsrc = 0;
+ } else {
+ usage();
+ }
+ }
+ argv++, argc--;
+ if (argc > 0 && (qdst = host_addr(argv[0]))) { /* Dest of path */
+ argv++, argc--;
+ if (argc > 0 && (qgrp = host_addr(argv[0]))) { /* Path via group */
+ argv++, argc--;
+ }
+ if (IN_MULTICAST(ntohl(qdst))) {
+ u_int32 temp = qdst;
+ qdst = qgrp;
+ qgrp = temp;
+ if (IN_MULTICAST(ntohl(qdst))) usage();
+ } else if (qgrp && !IN_MULTICAST(ntohl(qgrp))) usage();
+ }
+ }
+
+ if (passive) {
+ passive_mode();
+ return(0);
+ }
+
+ if (argc > 0) {
+ usage();
+ }
+
+#ifdef SUNOS5
+ if (sendopts)
+ checkforsolarisbug();
+#endif
+
+ /*
+ * Set useful defaults for as many parameters as possible.
+ */
+
+ defgrp = 0; /* Default to no group */
+ query_cast = htonl(0xE0000002); /* All routers multicast addr */
+ resp_cast = htonl(0xE0000120); /* Mtrace response multicast addr */
+ if (qgrp == 0) {
+ if (!weak)
+ qgrp = defgrp;
+ if (printstats && numstats != 0 && !tunstats) {
+ /* Stats are useless without a group */
+ warnx(
+ "WARNING: no multicast group specified, so no statistics printed");
+ numstats = 0;
+ }
+ } else {
+ if (weak)
+ warnx(
+ "WARNING: group was specified so not performing \"weak\" mtrace");
+ }
+
+ /*
+ * Get default local address for multicasts to use in setting defaults.
+ */
+ addr.sin_family = AF_INET;
+#if (defined(BSD) && (BSD >= 199103))
+ addr.sin_len = sizeof(addr);
+#endif
+ addr.sin_addr.s_addr = qgrp ? qgrp : query_cast;
+ addr.sin_port = htons(2000); /* Any port above 1024 will do */
+
+ /*
+ * Note that getsockname() can return 0 on some systems
+ * (notably SunOS 5.x, x < 6). This is taken care of in
+ * get_netmask(). If the default multicast interface (set
+ * with the route for 224.0.0.0) is not the same as the
+ * hostname, mtrace -i [if_addr] will have to be used.
+ */
+ if (((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ||
+ (connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0) ||
+ getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0)
+ err(-1, "determining local address");
+
+#ifdef SUNOS5
+ /*
+ * SunOS 5.X prior to SunOS 2.6, getsockname returns 0 for udp socket.
+ * This call to sysinfo will return the hostname.
+ * If the default multicast interfface (set with the route
+ * for 224.0.0.0) is not the same as the hostname,
+ * mtrace -i [if_addr] will have to be used.
+ */
+ if (addr.sin_addr.s_addr == 0) {
+ char myhostname[MAXHOSTNAMELEN];
+ struct hostent *hp;
+ int error;
+
+ error = sysinfo(SI_HOSTNAME, myhostname, sizeof(myhostname));
+ if (error == -1)
+ err(1, "getting my hostname");
+
+ hp = gethostbyname(myhostname);
+ if (hp == NULL || hp->h_addrtype != AF_INET ||
+ hp->h_length != sizeof(addr.sin_addr))
+ err(1, "finding IP address for my hostname");
+
+ memcpy((char *)&addr.sin_addr.s_addr, hp->h_addr, hp->h_length);
+ }
+#endif
+
+ /*
+ * Default destination for path to be queried is the local host.
+ * When gateway specified, default destination is that gateway
+ * and default source is local host.
+ */
+ if (qdst == 0) {
+ qdst = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
+ dst_netmask = get_netmask(udp, &qdst);
+ if (gwy && (gwy & dst_netmask) != (qdst & dst_netmask) &&
+ !IN_MULTICAST(ntohl(gwy)))
+ qdst = gwy;
+ }
+ if (qsrc == 0 && gwy)
+ qsrc = lcl_addr ? lcl_addr : addr.sin_addr.s_addr;
+ if (qsrc == 0)
+ usage();
+ if (!dst_netmask)
+ dst_netmask = get_netmask(udp, &qdst);
+ close(udp);
+ if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr;
+
+ /*
+ * Initialize the seed for random query identifiers.
+ */
+ gettimeofday(&tv, 0);
+ seed = tv.tv_usec ^ lcl_addr;
+#ifdef SYSV
+ srand48(seed);
+#endif
+
+ /*
+ * Protect against unicast queries to mrouted versions that might crash.
+ * Also use the obsolete "can mtrace" neighbor bit to warn about
+ * older implementations.
+ */
+ if (gwy && !IN_MULTICAST(ntohl(gwy)))
+ if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0], NULL)) {
+ int flags = ntohl(incr[0].igmp.igmp_group.s_addr);
+ int version = flags & 0xFFFF;
+ int info = (flags & 0xFF0000) >> 16;
+
+ if (version == 0x0303 || version == 0x0503) {
+ printf("Don't use -g to address an mrouted 3.%d, it might crash\n",
+ (version >> 8) & 0xFF);
+ exit(0);
+ }
+ if ((info & 0x08) == 0) {
+ printf("mtrace: ");
+ print_host(gwy);
+ printf(" probably doesn't support mtrace, trying anyway...\n");
+ }
+ }
+
+ printf("Mtrace from %s to %s via group %s\n",
+ inet_fmt(qsrc, s1), inet_fmt(qdst, s2), inet_fmt(qgrp, s3));
+
+ if ((qdst & dst_netmask) == (qsrc & dst_netmask))
+ fprintf(stderr, "mtrace: Source & receiver appear to be directly connected\n");
+
+ /*
+ * If the response is to be a multicast address, make sure we
+ * are listening on that multicast address.
+ */
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, lcl_addr);
+ } else k_join(resp_cast, lcl_addr);
+
+ memset(&base, 0, sizeof(base));
+
+ /*
+ * If the destination is on the local net, the last-hop router can
+ * be found by multicast to the all-routers multicast group.
+ * Otherwise, use the group address that is the subject of the
+ * query since by definition the last-hop router will be a member.
+ * Set default TTLs for local remote multicasts.
+ */
+ if (gwy == 0)
+ if ((qdst & dst_netmask) == (lcl_addr & dst_netmask)) tdst = query_cast;
+ else tdst = qgrp;
+ else tdst = gwy;
+ if (tdst == 0 && qgrp == 0)
+ errx(1, "mtrace: weak mtrace requires -g if destination is not local.\n");
+
+ if (IN_MULTICAST(ntohl(tdst))) {
+ k_set_loop(1); /* If I am running on a router, I need to hear this */
+ if (tdst == query_cast) k_set_ttl(qttl ? qttl : 1);
+ else k_set_ttl(qttl ? qttl : MULTICAST_TTL1);
+ }
+
+ /*
+ * Try a query at the requested number of hops or MAXHOPS if unspecified.
+ */
+ if (qno == 0) {
+ hops = MAXHOPS;
+ tries = 1;
+ printf("Querying full reverse path... ");
+ fflush(stdout);
+ } else {
+ hops = qno;
+ tries = nqueries;
+ if (fflag)
+ printf("Querying full reverse path, starting at hop %d...", qno);
+ else
+ printf("Querying reverse path, maximum %d hops... ", qno);
+ fflush(stdout);
+ }
+ base.rtime = 0;
+ base.len = 0;
+ hopbyhop = FALSE;
+
+ recvlen = send_recv(tdst, IGMP_MTRACE, hops, tries, &base, mtrace_callback);
+
+ /*
+ * If the initial query was successful, print it. Otherwise, if
+ * the query max hop count is the default of zero, loop starting
+ * from one until there is no response for extrahops more hops. The
+ * extra hops allow getting past an mtrace-capable mrouter that can't
+ * send multicast packets because all phyints are disabled.
+ */
+ if (recvlen) {
+ printf("\n 0 ");
+ print_host(qdst);
+ printf("\n");
+ print_trace(1, &base, names);
+ r = base.resps + base.len - 1;
+ if (r->tr_rflags == TR_OLD_ROUTER || r->tr_rflags == TR_NO_SPACE ||
+ (qno != 0 && r->tr_rmtaddr != 0 && !fflag)) {
+ printf("%3d ", -(base.len+1));
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
+ } else {
+ if (fflag) {
+ nexthop = hops = qno;
+ goto continuehop;
+ }
+ VAL_TO_MASK(smask, r->tr_smask);
+ if ((r->tr_inaddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(base.len+1));
+ print_host(qsrc);
+ printf("\n");
+ }
+ }
+ } else if (qno == 0) {
+ hopbyhop = TRUE;
+ printf("switching to hop-by-hop:\n 0 ");
+ print_host(qdst);
+ printf("\n");
+
+ for (hops = 1, nexthop = 1; hops <= MAXHOPS; ++hops) {
+ printf("%3d ", -hops);
+ fflush(stdout);
+
+ /*
+ * After a successful first hop, try switching to the unicast
+ * address of the last-hop router instead of multicasting the
+ * trace query. This should be safe for mrouted versions 3.3
+ * and 3.5 because there is a long route timeout with metric
+ * infinity before a route disappears. Switching to unicast
+ * reduces the amount of multicast traffic and avoids a bug
+ * with duplicate suppression in mrouted 3.5.
+ */
+ if (hops == 2 && gwy == 0 && lastout != 0 &&
+ (recvlen = send_recv(lastout, IGMP_MTRACE, hops, 1, &base, mtrace_callback)))
+ tdst = lastout;
+ else recvlen = send_recv(tdst, IGMP_MTRACE, hops, nqueries, &base, mtrace_callback);
+
+ if (recvlen == 0) {
+ /*if (hops == 1) break;*/
+ if (hops == nexthop) {
+ if (hops == 1) {
+ printf("\n");
+ } else if (what_kind(&base, "didn't respond")) {
+ /* the ask_neighbors determined that the
+ * not-responding router is the first-hop. */
+ break;
+ }
+ if (extrahops == 0)
+ break;
+ } else if (hops < nexthop + extrahops) {
+ printf("\n");
+ } else {
+ printf("...giving up\n");
+ break;
+ }
+ continue;
+ }
+ if (base.len == hops &&
+ (hops == 1 || (base.resps+nexthop-2)->tr_outaddr == lastout)) {
+ if (hops == nexthop) {
+ print_trace(-hops, &base, names);
+ } else {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base, names);
+ }
+ } else {
+ if (base.len < hops) {
+ /*
+ * A shorter trace than requested means a fatal error
+ * occurred along the path, or that the route changed
+ * to a shorter one.
+ *
+ * If the trace is longer than the last one we received,
+ * then we are resuming from a skipped router (but there
+ * is still probably a problem).
+ *
+ * If the trace is shorter than the last one we
+ * received, then the route must have changed (and
+ * there is still probably a problem).
+ */
+ if (nexthop <= base.len) {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base, names);
+ } else if (nexthop > base.len + 1) {
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base, names);
+ }
+ } else {
+ /*
+ * The last hop address is not the same as it was.
+ * If we didn't know the last hop then we just
+ * got the first response from a hop-by-hop trace;
+ * if we did know the last hop then
+ * the route probably changed underneath us.
+ */
+ hops = base.len;
+ if (lastout != 0)
+ printf("\nRoute must have changed...\n");
+ else
+ printf("\nResuming...\n");
+ print_trace(1, &base, names);
+ }
+ }
+continuehop:
+ r = base.resps + base.len - 1;
+ lastout = r->tr_outaddr;
+
+ if (base.len < hops ||
+ r->tr_rmtaddr == 0 ||
+ (r->tr_rflags & 0x80)) {
+ VAL_TO_MASK(smask, r->tr_smask);
+ if (r->tr_rmtaddr) {
+ if (hops != nexthop) {
+ printf("\n%3d ", -(base.len+1));
+ }
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace" :
+ "would be the next hop");
+ /* XXX could do segmented trace if TR_NO_SPACE */
+ } else if (r->tr_rflags == TR_NO_ERR &&
+ (r->tr_inaddr & smask) == (qsrc & smask)) {
+ printf("%3d ", -(hops + 1));
+ print_host(qsrc);
+ printf("\n");
+ }
+ break;
+ }
+
+ nexthop = hops + 1;
+ }
+ }
+
+ if (base.rtime == 0) {
+ printf("Timed out receiving responses\n");
+ if (IN_MULTICAST(ntohl(tdst)))
+ if (tdst == query_cast)
+ printf("Perhaps no local router has a route for source %s\n",
+ inet_fmt(qsrc, s1));
+ else
+ printf("Perhaps receiver %s is not a member of group %s,\n\
+or no router local to it has a route for source %s,\n\
+or multicast at ttl %d doesn't reach its last-hop router for that source\n",
+ inet_fmt(qdst, s2), inet_fmt(qgrp, s3), inet_fmt(qsrc, s1),
+ qttl ? qttl : MULTICAST_TTL1);
+ exit(1);
+ }
+
+ printf("Round trip time %d ms; ", t_diff(base.rtime, base.qtime));
+ {
+ struct tr_resp *n = base.resps + base.len - 1;
+ u_int ttl = n->tr_fttl + 1;
+
+ rno = base.len - 1;
+ while (--rno > 0) {
+ --n;
+ ttl = MaX(ttl, MaX(1, n->tr_fttl) + base.len - rno);
+ }
+ printf("total ttl of %d required.\n\n",ttl);
+ }
+
+ /*
+ * Use the saved response which was the longest one received,
+ * and make additional probes after delay to measure loss.
+ */
+ raddr = base.qhdr.tr_raddr;
+ rttl = TR_GETTTL(base.qhdr.tr_rttlqid);
+ gettimeofday(&tv, 0);
+ waittime = statint - (((tv.tv_sec + JAN_1970) & 0xFFFF) - (base.qtime >> 16));
+ prev = &base;
+ new = &incr[numstats&1];
+
+ /*
+ * Zero out bug-avoidance counters
+ */
+ memset(bugs, 0, sizeof(bugs));
+
+ if (!printstats)
+ printf("Monitoring path..");
+
+ while (numstats--) {
+ if (waittime < 1) printf("\n");
+ else {
+ if (printstats && (lossthresh == 0 || printed)) {
+ printf("Waiting to accumulate statistics...");
+ } else {
+ printf(".");
+ }
+ fflush(stdout);
+ sleep((unsigned)waittime);
+ }
+ printed = 0;
+ rno = hopbyhop ? base.len : qno ? qno : MAXHOPS;
+ recvlen = send_recv(tdst, IGMP_MTRACE, rno, nqueries, new, mtrace_callback);
+
+ if (recvlen == 0) {
+ printf("Timed out.\n");
+ if (numstats) {
+ numstats++;
+ continue;
+ } else
+ exit(1);
+ }
+
+ if (base.len != new->len || path_changed(&base, new)) {
+ printf("%s", base.len == new->len ? "Route changed" :
+ "Trace length doesn't match");
+ if (!printstats)
+ printf(" after %d seconds",
+ (int)((new->qtime - base.qtime) >> 16));
+ printf(":\n");
+printandcontinue:
+ print_trace(1, new, names);
+ numstats++;
+ bcopy(new, &base, sizeof(base));
+ nexthop = hops = new->len;
+ printf("Continuing with hop-by-hop...\n");
+ goto continuehop;
+ }
+
+ if (printstats) {
+ if (new->igmp.igmp_group.s_addr != qgrp ||
+ new->qhdr.tr_src != qsrc || new->qhdr.tr_dst != qdst)
+ printf("\nWARNING: trace modified en route; statistics may be incorrect\n");
+ fixup_stats(&base, prev, new, bugs);
+ if ((lossthresh == 0) || check_thresh(lossthresh, &base, prev, new)) {
+ printf("Results after %d seconds",
+ (int)((new->qtime - base.qtime) >> 16));
+ if (lossthresh)
+ printf(" (this trace %d seconds)",
+ (int)((new->qtime - prev->qtime) >> 16));
+ if (verbose) {
+ time_t t = time(0);
+ struct tm *qr = localtime(&t);
+
+ printf(" qid 0x%06x at %2d:%02d:%02d",
+ TR_GETQID(base.qhdr.tr_rttlqid),
+ qr->tm_hour, qr->tm_min, qr->tm_sec);
+ }
+ printf(":\n\n");
+ printed = 1;
+ if (print_stats(&base, prev, new, bugs, names)) {
+ printf("This should have been detected earlier, but ");
+ printf("Route changed:\n");
+ goto printandcontinue;
+ }
+ }
+ }
+ prev = new;
+ new = &incr[numstats&1];
+ waittime = statint;
+ }
+
+ /*
+ * If the response was multicast back, leave the group
+ */
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_leave(raddr, lcl_addr);
+ } else k_leave(resp_cast, lcl_addr);
+
+ return (0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: mtrace [-MUOPTWVlnpvs] [-e extra_hops] [-f first_hop] [-i if_addr]",
+ " [-g gateway] [-m max_hops] [-q nqueries] [-r resp_dest]",
+ " [-S statint] [-t ttl] [-w wait] source [receiver] [group]");
+ exit(1);
+}
+
+void
+check_vif_state()
+{
+ log(LOG_WARNING, errno, "sendto");
+}
+
+/*
+ * Log errors and other messages to stderr, according to the severity
+ * of the message and the current debug level. For errors of severity
+ * LOG_ERR or worse, terminate the program.
+ */
+#ifdef __STDC__
+void
+log(int severity, int syserr, char *format, ...)
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap, format);
+#else
+/*VARARGS3*/
+void
+log(severity, syserr, format, va_alist)
+ int severity, syserr;
+ char *format;
+ va_dcl
+{
+ va_list ap;
+ char fmt[100];
+
+ va_start(ap);
+#endif
+
+ switch (debug) {
+ case 0: if (severity > LOG_WARNING) return;
+ case 1: if (severity > LOG_NOTICE) return;
+ case 2: if (severity > LOG_INFO ) return;
+ default:
+ fmt[0] = '\0';
+ if (severity == LOG_WARNING)
+ strcpy(fmt, "warning - ");
+ strncat(fmt, format, sizeof(fmt)-strlen(fmt));
+ fmt[sizeof(fmt)-1]='\0';
+ vfprintf(stderr, fmt, ap);
+ if (syserr == 0)
+ fprintf(stderr, "\n");
+ else if (syserr < sys_nerr)
+ fprintf(stderr, ": %s\n", sys_errlist[syserr]);
+ else
+ fprintf(stderr, ": errno %d\n", syserr);
+ }
+ if (severity <= LOG_ERR) exit(1);
+}
diff --git a/usr.sbin/mrouted/mtrace.h b/usr.sbin/mrouted/mtrace.h
new file mode 100644
index 0000000..e67dbd0
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.h
@@ -0,0 +1,89 @@
+/*
+ * Multicast traceroute related definitions
+ *
+ * mtrace.h,v 5.2 1998/12/04 04:48:21 fenner Exp
+ */
+
+/*
+ * NetBSD renamed the mtrace packet types.
+ */
+#if !defined(IGMP_MTRACE_RESP) && defined(IGMP_MTRACE_REPLY)
+#define IGMP_MTRACE_RESP IGMP_MTRACE_REPLY
+#define IGMP_MTRACE IGMP_MTRACE_QUERY
+#endif
+
+/*
+ * The packet format for a traceroute request.
+ */
+struct tr_query {
+ u_int32 tr_src; /* traceroute source */
+ u_int32 tr_dst; /* traceroute destination */
+ u_int32 tr_raddr; /* traceroute response address */
+ u_int32 tr_rttlqid; /* response ttl and qid */
+};
+
+#define TR_SETTTL(x, ttl) (x = (x & 0x00ffffff) | ((ttl) << 24))
+#define TR_GETTTL(x) (((x) >> 24) & 0xff)
+#define TR_SETQID(x, qid) (x = (x & 0xff000000) | ((qid) & 0x00ffffff))
+#define TR_GETQID(x) ((x) & 0x00ffffff)
+
+/*
+ * Traceroute response format. A traceroute response has a tr_query at the
+ * beginning, followed by one tr_resp for each hop taken.
+ */
+struct tr_resp {
+ u_int32 tr_qarr; /* query arrival time */
+ u_int32 tr_inaddr; /* incoming interface address */
+ u_int32 tr_outaddr; /* outgoing interface address */
+ u_int32 tr_rmtaddr; /* parent address in source tree */
+ u_int32 tr_vifin; /* input packet count on interface */
+ u_int32 tr_vifout; /* output packet count on interface */
+ u_int32 tr_pktcnt; /* total incoming packets for src-grp */
+ u_char tr_rproto; /* routing protocol deployed on router */
+ u_char tr_fttl; /* ttl required to forward on outvif */
+ u_char tr_smask; /* subnet mask for src addr */
+ u_char tr_rflags; /* forwarding error codes */
+};
+
+/* defs within mtrace */
+#define QLEN sizeof(struct tr_query)
+#define RLEN sizeof(struct tr_resp)
+
+/* fields for tr_rflags (forwarding error codes) */
+#define TR_NO_ERR 0
+#define TR_WRONG_IF 1
+#define TR_PRUNED 2
+#define TR_OPRUNED 3
+#define TR_SCOPED 4
+#define TR_NO_RTE 5
+#define TR_NO_FWD 7
+#define TR_HIT_RP 8
+#define TR_RPF_IF 9
+#define TR_NO_MULTI 10
+#define TR_NO_SPACE 0x81
+#define TR_OLD_ROUTER 0x82
+#define TR_ADMIN_PROHIB 0x83
+
+/* fields for tr_rproto (routing protocol) */
+#define PROTO_DVMRP 1
+#define PROTO_MOSPF 2
+#define PROTO_PIM 3
+#define PROTO_CBT 4
+#define PROTO_PIM_SPECIAL 5
+#define PROTO_PIM_STATIC 6
+#define PROTO_DVMRP_STATIC 7
+#define PROTO_PIM_BGP4PLUS 8
+#define PROTO_CBT_SPECIAL 9
+#define PROTO_CBT_STATIC 10
+#define PROTO_PIM_ASSERT 11
+
+#define VAL_TO_MASK(x, i) { \
+ x = htonl(~((1 << (32 - (i))) - 1)); \
+ };
+
+#if defined(__STDC__) || defined(__GNUC__)
+#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
+#else
+#define JAN_1970 2208988800L /* 1970 - 1900 in seconds */
+#define const /**/
+#endif
diff --git a/usr.sbin/mrouted/mtrace/Makefile b/usr.sbin/mrouted/mtrace/Makefile
new file mode 100644
index 0000000..ee9adb7
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= mtrace
+MAN= mtrace.8
+BINOWN= root
+BINMODE= 4555
+
+CFLAGS+= -I$S
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/pathnames.h b/usr.sbin/mrouted/pathnames.h
new file mode 100644
index 0000000..d4fb01d
--- /dev/null
+++ b/usr.sbin/mrouted/pathnames.h
@@ -0,0 +1,26 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * pathnames.h,v 3.8 1995/11/29 22:36:57 fenner Rel
+ */
+
+#define _PATH_MROUTED_CONF "/etc/mrouted.conf"
+
+#if (defined(BSD) && (BSD >= 199103))
+#define _PATH_MROUTED_PID "/var/run/mrouted.pid"
+#define _PATH_MROUTED_GENID "/var/run/mrouted.genid"
+#define _PATH_MROUTED_DUMP "/var/tmp/mrouted.dump"
+#define _PATH_MROUTED_CACHE "/var/tmp/mrouted.cache"
+#else
+#define _PATH_MROUTED_PID "/etc/mrouted.pid"
+#define _PATH_MROUTED_GENID "/etc/mrouted.genid"
+#define _PATH_MROUTED_DUMP "/usr/tmp/mrouted.dump"
+#define _PATH_MROUTED_CACHE "/usr/tmp/mrouted.cache"
+#endif
diff --git a/usr.sbin/mrouted/prune.c b/usr.sbin/mrouted/prune.c
new file mode 100644
index 0000000..acefaf9
--- /dev/null
+++ b/usr.sbin/mrouted/prune.c
@@ -0,0 +1,2619 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * prune.c,v 3.8.4.59 1998/03/01 02:06:32 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+extern int cache_lifetime;
+extern int prune_lifetime;
+extern struct rtentry *routing_table;
+
+extern int phys_vif;
+
+extern int allow_black_holes;
+
+/*
+ * randomize value to obtain a value between .5x and 1.5x
+ * in order to prevent synchronization
+ */
+#ifdef SYSV
+#define JITTERED_VALUE(x) ((x)/2 + (lrand48() % (x)))
+#else
+#define JITTERED_VALUE(x) ((x)/2 + (arc4random() % (x)))
+#endif
+#define CACHE_LIFETIME(x) JITTERED_VALUE(x) /* XXX */
+
+struct gtable *kernel_table; /* ptr to list of kernel grp entries*/
+static struct gtable *kernel_no_route; /* list of grp entries w/o routes */
+struct gtable *gtp; /* pointer for kernel rt entries */
+unsigned int kroutes; /* current number of cache entries */
+
+/****************************************************************************
+ Functions that are local to prune.c
+****************************************************************************/
+static int scoped_addr __P((vifi_t vifi, u_int32 addr));
+static void prun_add_ttls __P((struct gtable *gt));
+static int pruning_neighbor __P((vifi_t vifi, u_int32 addr));
+static int can_mtrace __P((vifi_t vifi, u_int32 addr));
+static struct ptable * find_prune_entry __P((u_int32 vr, struct ptable *pt));
+static void remove_sources __P((struct gtable *gt));
+static void rexmit_prune __P((void *arg));
+static void expire_prune __P((vifi_t vifi, struct gtable *gt));
+static void send_prune __P((struct gtable *gt));
+static void send_graft __P((struct gtable *gt));
+static void send_graft_ack __P((u_int32 src, u_int32 dst,
+ u_int32 origin, u_int32 grp,
+ vifi_t vifi));
+static void update_kernel __P((struct gtable *g));
+
+/*
+ * Updates the ttl values for each vif.
+ */
+static void
+prun_add_ttls(gt)
+ struct gtable *gt;
+{
+ struct uvif *v;
+ vifi_t vifi;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (VIFM_ISSET(vifi, gt->gt_grpmems))
+ gt->gt_ttls[vifi] = v->uv_threshold;
+ else
+ gt->gt_ttls[vifi] = 0;
+ }
+}
+
+/*
+ * checks for scoped multicast addresses
+ * XXX I want to make the check of allow_black_holes based on ALLOW_BLACK_HOLES
+ * but macros are not functions.
+ */
+#define GET_SCOPE(gt) { \
+ register vifi_t _i; \
+ VIFM_CLRALL((gt)->gt_scope); \
+ if (allow_black_holes || \
+ (ntohl((gt)->gt_mcastgrp) & 0xff000000) == 0xef000000) \
+ for (_i = 0; _i < numvifs; _i++) \
+ if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
+ VIFM_SET(_i, (gt)->gt_scope); \
+ } \
+ if ((gt)->gt_route == NULL || ((gt)->gt_route->rt_parent != NO_VIF && \
+ VIFM_ISSET((gt)->gt_route->rt_parent, (gt)->gt_scope))) \
+ VIFM_SETALL((gt)->gt_scope);
+
+#define APPLY_SCOPE(gt) VIFM_CLR_MASK((gt)->gt_grpmems, (gt)->gt_scope)
+
+#define GET_MEMBERSHIP(gt, vifi) { \
+ if ((gt)->gt_route && \
+ VIFM_ISSET((vifi), (gt)->gt_route->rt_children) && \
+ (!SUBS_ARE_PRUNED((gt)->gt_route->rt_subordinates, \
+ uvifs[vifi].uv_nbrmap, (gt)->gt_prunes) || \
+ grplst_mem((vifi), (gt)->gt_mcastgrp))) \
+ VIFM_SET((vifi), (gt)->gt_grpmems); \
+ }
+
+static int
+scoped_addr(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct vif_acl *acl;
+
+ for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next)
+ if ((addr & acl->acl_mask) == acl->acl_addr)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Determine the list of outgoing vifs, based upon
+ * route subordinates, prunes received, and group
+ * memberships.
+ */
+void
+determine_forwvifs(gt)
+ struct gtable *gt;
+{
+ vifi_t i;
+
+ VIFM_CLRALL(gt->gt_grpmems);
+ for (i = 0; i < numvifs; i++) {
+ GET_MEMBERSHIP(gt, i);
+ }
+ GET_SCOPE(gt);
+ APPLY_SCOPE(gt);
+}
+
+/*
+ * Send a prune or a graft if necessary.
+ */
+void
+send_prune_or_graft(gt)
+ struct gtable *gt;
+{
+ if (VIFM_ISEMPTY(gt->gt_grpmems))
+ send_prune(gt);
+ else if (gt->gt_prsent_timer)
+ send_graft(gt);
+}
+
+/*
+ * Determine if mcastgrp has a listener on vifi
+ */
+int
+grplst_mem(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ register struct listaddr *g;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ if (mcastgrp == g->al_addr)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Finds the group entry with the specified source and netmask.
+ * If netmask is 0, it uses the route's netmask.
+ *
+ * Returns TRUE if found a match, and the global variable gtp is left
+ * pointing to entry before the found entry.
+ * Returns FALSE if no exact match found, gtp is left pointing to before
+ * the entry in question belongs, or is NULL if the it belongs at the
+ * head of the list.
+ */
+int
+find_src_grp(src, mask, grp)
+ u_int32 src;
+ u_int32 mask;
+ u_int32 grp;
+{
+ struct gtable *gt;
+
+ gtp = NULL;
+ gt = kernel_table;
+ while (gt != NULL) {
+ if (grp == gt->gt_mcastgrp &&
+ (mask ? (gt->gt_route->rt_origin == src &&
+ gt->gt_route->rt_originmask == mask) :
+ ((src & gt->gt_route->rt_originmask) ==
+ gt->gt_route->rt_origin)))
+ return TRUE;
+ if (ntohl(grp) > ntohl(gt->gt_mcastgrp) ||
+ (grp == gt->gt_mcastgrp &&
+ (ntohl(mask) < ntohl(gt->gt_route->rt_originmask) ||
+ (mask == gt->gt_route->rt_originmask &&
+ (ntohl(src) > ntohl(gt->gt_route->rt_origin)))))) {
+ gtp = gt;
+ gt = gt->gt_gnext;
+ }
+ else break;
+ }
+ return FALSE;
+}
+
+/*
+ * Check if the neighbor supports pruning
+ */
+static int
+pruning_neighbor(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct listaddr *n = neighbor_info(vifi, addr);
+ int vers;
+
+ if (n == NULL)
+ return 0;
+
+ vers = NBR_VERS(n);
+ return (vers >= 0x0300 && ((vers & 0xff00) != 0x0a00));
+}
+
+/*
+ * Can the neighbor in question handle multicast traceroute?
+ */
+static int
+can_mtrace(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct listaddr *n = neighbor_info(vifi, addr);
+ int vers;
+
+ if (n == NULL)
+ return 1; /* fail "safe" */
+
+ vers = NBR_VERS(n);
+ return (vers >= 0x0303 && ((vers & 0xff00) != 0x0a00));
+}
+
+/*
+ * Returns the prune entry of the router, or NULL if none exists
+ */
+static struct ptable *
+find_prune_entry(vr, pt)
+ u_int32 vr;
+ struct ptable *pt;
+{
+ while (pt) {
+ if (pt->pt_router == vr)
+ return pt;
+ pt = pt->pt_next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Remove all the sources hanging off the group table entry from the kernel
+ * cache. Remember the packet counts wherever possible, to keep the mtrace
+ * counters consistent. This prepares for possible prune retransmission,
+ * either on a multi-access network or when a prune that we sent upstream
+ * has expired.
+ */
+static void
+remove_sources(gt)
+ struct gtable *gt;
+{
+ struct stable *st;
+ struct sioc_sg_req sg_req;
+
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+
+ /*
+ * call k_del_rg() on every one of the gt->gt_srctbl entries
+ * but first save the packet count so that the mtrace packet
+ * counters can remain approximately correct. There's a race
+ * here but it's minor.
+ */
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ if (st->st_ctime == 0)
+ continue;
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "rexmit_prune deleting (%s %s) (next is %d sec)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prune_rexmit);
+ sg_req.src.s_addr = st->st_origin;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ sg_req.pktcnt = 0;
+ }
+ k_del_rg(st->st_origin, gt);
+ st->st_ctime = 0; /* flag that it's not in the kernel any more */
+ st->st_savpkt += sg_req.pktcnt;
+ kroutes--;
+ }
+
+ /*
+ * Now, add_table_entry will prune when asked to add a cache entry.
+ */
+}
+
+/*
+ * Prepare for possible prune retransmission
+ */
+static void
+rexmit_prune(arg)
+ void *arg;
+{
+ struct gtable *gt = *(struct gtable **)arg;
+
+ free(arg);
+
+ gt->gt_rexmit_timer = 0;
+
+ /* Make sure we're still not forwarding traffic */
+ if (!VIFM_ISEMPTY(gt->gt_grpmems)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "rexmit_prune (%s %s): gm:%x",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_grpmems);
+ return;
+ }
+
+ remove_sources(gt);
+}
+
+/*
+ * Send a prune message to the dominant router for
+ * this source.
+ *
+ * Record an entry that a prune was sent for this group
+ */
+static void
+send_prune(gt)
+ struct gtable *gt;
+{
+ struct ptable *pt;
+ char *p;
+ int i;
+ int datalen;
+ u_int32 dst;
+ u_int32 tmp;
+ int rexmitting = 0;
+ struct uvif *v;
+
+ /*
+ * Can't process a prune if we don't have an associated route
+ * or if the route points to a local interface.
+ */
+ if (gt->gt_route == NULL || gt->gt_route->rt_parent == NO_VIF ||
+ gt->gt_route->rt_gateway == 0)
+ return;
+
+ /* Don't send a prune to a non-pruning router */
+ if (!pruning_neighbor(gt->gt_route->rt_parent, gt->gt_route->rt_gateway))
+ return;
+
+ v = &uvifs[gt->gt_route->rt_parent];
+ /*
+ * sends a prune message to the router upstream.
+ */
+#if 0
+ dst = v->uv_flags & VIFF_TUNNEL ? dvmrp_group : gt->gt_route->rt_gateway; /*XXX*/
+#else
+ dst = gt->gt_route->rt_gateway;
+#endif
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ /*
+ * determine prune lifetime, if this isn't a retransmission.
+ *
+ * Use interface-specified lifetime if there is one.
+ */
+ if (gt->gt_prsent_timer == 0) {
+ int l = prune_lifetime;
+
+ if (v->uv_prune_lifetime != 0)
+ l = v->uv_prune_lifetime;
+
+ gt->gt_prsent_timer = JITTERED_VALUE(l);
+ for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next)
+ if (pt->pt_timer < gt->gt_prsent_timer)
+ gt->gt_prsent_timer = pt->pt_timer;
+ } else if (gt->gt_prsent_timer < 0) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "asked to rexmit? (%s,%s)/%d on vif %d to %s with negative time",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ } else
+ rexmitting = 1;
+
+ if (rexmitting && !(v->uv_flags & VIFF_REXMIT_PRUNES)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "not rexmitting prune for (%s %s)/%d on vif %d to %s",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ }
+ if (gt->gt_prsent_timer <= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "not bothering to send prune for (%s,%s)/%d on vif %d to %s because it's too short",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+ return;
+ }
+
+ /*
+ * If we have a graft pending, cancel graft retransmission
+ */
+ gt->gt_grftsnt = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_route->rt_origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_mcastgrp))[i];
+ tmp = htonl(gt->gt_prsent_timer);
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(tmp))[i];
+ datalen += 12;
+
+ send_on_vif(v, dst, DVMRP_PRUNE, datalen);
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s prune for (%s %s)/%d on vif %d to %s",
+ rexmitting ? "rexmitted" : "sent",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+
+ if ((v->uv_flags & VIFF_REXMIT_PRUNES) &&
+ gt->gt_rexmit_timer == 0 &&
+ gt->gt_prsent_timer > gt->gt_prune_rexmit) {
+ struct gtable **arg =
+ (struct gtable **)malloc(sizeof (struct gtable **));
+
+ *arg = gt;
+ gt->gt_rexmit_timer = timer_setTimer(
+ JITTERED_VALUE(gt->gt_prune_rexmit),
+ rexmit_prune, arg);
+ gt->gt_prune_rexmit *= 2;
+ }
+}
+
+/*
+ * a prune was sent upstream
+ * so, a graft has to be sent to annul the prune
+ * set up a graft timer so that if an ack is not
+ * heard within that time, another graft request
+ * is sent out.
+ */
+static void
+send_graft(gt)
+ struct gtable *gt;
+{
+ register char *p;
+ register int i;
+ int datalen;
+ u_int32 dst;
+
+ /* Can't send a graft without an associated route */
+ if (gt->gt_route == NULL || gt->gt_route->rt_parent == NO_VIF) {
+ gt->gt_grftsnt = 0;
+ return;
+ }
+
+ gt->gt_prsent_timer = 0;
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
+ if (gt->gt_grftsnt == 0)
+ gt->gt_grftsnt = 1;
+
+#if 0
+ dst = uvifs[gt->gt_route->rt_parent].uv_flags & VIFF_TUNNEL ? dvmrp_group : gt->gt_route->rt_gateway; /*XXX*/
+#else
+ dst = gt->gt_route->rt_gateway;
+#endif
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_route->rt_origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(gt->gt_mcastgrp))[i];
+ datalen += 8;
+
+ send_on_vif(&uvifs[gt->gt_route->rt_parent], dst, DVMRP_GRAFT, datalen);
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "sent graft for (%s %s) to %s on vif %d",
+ RT_FMT(gt->gt_route, s1), inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(gt->gt_route->rt_gateway, s3), gt->gt_route->rt_parent);
+}
+
+/*
+ * Send an ack that a graft was received
+ */
+static void
+send_graft_ack(src, dst, origin, grp, vifi)
+ u_int32 src;
+ u_int32 dst;
+ u_int32 origin;
+ u_int32 grp;
+ vifi_t vifi;
+{
+ register char *p;
+ register int i;
+ int datalen;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(origin))[i];
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(grp))[i];
+ datalen += 8;
+
+ if (vifi == NO_VIF)
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
+ htonl(MROUTED_LEVEL), datalen);
+ else {
+#if 0
+ if (uvifs[vifi].uv_flags & VIFF_TUNNEL)
+ dst = dvmrp_group; /* XXX */
+#endif
+ send_on_vif(&uvifs[vifi], dst, DVMRP_GRAFT_ACK, datalen);
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
+ if (vifi == NO_VIF)
+ log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s",
+ inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3));
+ else
+ log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s on vif %d",
+ inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3), vifi);
+}
+
+/*
+ * Update the kernel cache with all the routes hanging off the group entry
+ */
+static void
+update_kernel(g)
+ struct gtable *g;
+{
+ struct stable *st;
+
+ for (st = g->gt_srctbl; st; st = st->st_next)
+ if (st->st_ctime != 0)
+ k_add_rg(st->st_origin, g);
+}
+
+/****************************************************************************
+ Functions that are used externally
+****************************************************************************/
+
+#ifdef SNMP
+#include <sys/types.h>
+#include "snmp.h"
+
+/*
+ * Find a specific group entry in the group table
+ */
+struct gtable *
+find_grp(grp)
+ u_int32 grp;
+{
+ struct gtable *gt;
+
+ for (gt = kernel_table; gt; gt = gt->gt_gnext) {
+ if (ntohl(grp) < ntohl(gt->gt_mcastgrp))
+ break;
+ if (gt->gt_mcastgrp == grp)
+ return gt;
+ }
+ return NULL;
+}
+
+/*
+ * Given a group entry and source, find the corresponding source table
+ * entry
+ */
+struct stable *
+find_grp_src(gt, src)
+ struct gtable *gt;
+ u_int32 src;
+{
+ struct stable *st;
+ u_long grp = gt->gt_mcastgrp;
+ struct gtable *gtcurr;
+
+ for (gtcurr = gt; gtcurr->gt_mcastgrp == grp; gtcurr = gtcurr->gt_gnext) {
+ for (st = gtcurr->gt_srctbl; st; st = st->st_next)
+ if (st->st_origin == src)
+ return st;
+ }
+ return NULL;
+}
+
+/*
+ * Find next entry > specification
+ */
+int
+next_grp_src_mask(gtpp, stpp, grp, src, mask)
+ struct gtable **gtpp; /* ordered by group */
+ struct stable **stpp; /* ordered by source */
+ u_int32 grp;
+ u_int32 src;
+ u_int32 mask;
+{
+ struct gtable *gt, *gbest = NULL;
+ struct stable *st, *sbest = NULL;
+
+ /* Find first group entry >= grp spec */
+ (*gtpp) = kernel_table;
+ while ((*gtpp) && ntohl((*gtpp)->gt_mcastgrp) < ntohl(grp))
+ (*gtpp)=(*gtpp)->gt_gnext;
+ if (!(*gtpp))
+ return 0; /* no more groups */
+
+ for (gt = kernel_table; gt; gt=gt->gt_gnext) {
+ /* Since grps are ordered, we can stop when group changes from gbest */
+ if (gbest && gbest->gt_mcastgrp != gt->gt_mcastgrp)
+ break;
+ for (st = gt->gt_srctbl; st; st=st->st_next) {
+
+ /* Among those entries > spec, find "lowest" one */
+ if (((ntohl(gt->gt_mcastgrp)> ntohl(grp))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
+ && ntohl(st->st_origin)> ntohl(src))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(grp)
+ && ntohl(st->st_origin)==src && 0xFFFFFFFF>ntohl(mask)))
+ && (!gbest
+ || (ntohl(gt->gt_mcastgrp)< ntohl(gbest->gt_mcastgrp))
+ || (ntohl(gt->gt_mcastgrp)==ntohl(gbest->gt_mcastgrp)
+ && ntohl(st->st_origin)< ntohl(sbest->st_origin)))) {
+ gbest = gt;
+ sbest = st;
+ }
+ }
+ }
+ (*gtpp) = gbest;
+ (*stpp) = sbest;
+ return (*gtpp)!=0;
+}
+
+/*
+ * Ensure that sg contains current information for the given group,source.
+ * This is fetched from the kernel as a unit so that counts for the entry
+ * are consistent, i.e. packet and byte counts for the same entry are
+ * read at the same time.
+ */
+void
+refresh_sg(sg, gt, st)
+ struct sioc_sg_req *sg;
+ struct gtable *gt;
+ struct stable *st;
+{
+ static int lastq = -1;
+
+ if (quantum != lastq || sg->src.s_addr!=st->st_origin
+ || sg->grp.s_addr!=gt->gt_mcastgrp) {
+ lastq = quantum;
+ sg->src.s_addr = st->st_origin;
+ sg->grp.s_addr = gt->gt_mcastgrp;
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)sg);
+ }
+}
+
+/*
+ * Given a routing table entry, and a vifi, find the next entry
+ * equal to or greater than those
+ */
+int
+next_child(gtpp, stpp, grp, src, mask, vifi)
+ struct gtable **gtpp;
+ struct stable **stpp;
+ u_int32 grp;
+ u_int32 src;
+ u_int32 mask;
+ vifi_t *vifi; /* vif at which to start looking */
+{
+ /* Get (G,S,M) entry */
+ if (mask!=0xFFFFFFFF
+ || !((*gtpp) = find_grp(grp))
+ || !((*stpp) = find_grp_src((*gtpp),src)))
+ if (!next_grp_src_mask(gtpp, stpp, grp, src, mask))
+ return 0;
+
+ /* Continue until we get one with a valid next vif */
+ do {
+ for (; (*gtpp)->gt_route->rt_children && *vifi<numvifs; (*vifi)++)
+ if (VIFM_ISSET(*vifi, (*gtpp)->gt_route->rt_children))
+ return 1;
+ *vifi = 0;
+ } while (next_grp_src_mask(gtpp, stpp, (*gtpp)->gt_mcastgrp,
+ (*stpp)->st_origin, 0xFFFFFFFF) );
+
+ return 0;
+}
+#endif /* SNMP */
+
+/*
+ * Initialize the kernel table structure
+ */
+void
+init_ktable()
+{
+ kernel_table = NULL;
+ kernel_no_route = NULL;
+ kroutes = 0;
+}
+
+/*
+ * Add a new table entry for (origin, mcastgrp)
+ */
+void
+add_table_entry(origin, mcastgrp)
+ u_int32 origin;
+ u_int32 mcastgrp;
+{
+ struct rtentry *r;
+ struct gtable *gt,**gtnp,*prev_gt;
+ struct stable *st,**stnp;
+
+ /*
+ * Since we have to enable mrouting to get the version number,
+ * some cache creation requests can sneak through. Ignore them
+ * since we're not going to do useful stuff until we've performed
+ * final initialization.
+ */
+ if (!did_final_init)
+ return;
+
+#ifdef DEBUG_MFC
+ md_log(MD_MISS, origin, mcastgrp);
+#endif
+
+ r = determine_route(origin);
+ prev_gt = NULL;
+ if (r == NULL) {
+ /*
+ * Look for it on the no_route table; if it is found then
+ * it will be detected as a duplicate below.
+ */
+ for (gt = kernel_no_route; gt; gt = gt->gt_next)
+ if (mcastgrp == gt->gt_mcastgrp &&
+ gt->gt_srctbl && gt->gt_srctbl->st_origin == origin)
+ break;
+ gtnp = &kernel_no_route;
+ } else {
+ gtnp = &r->rt_groups;
+ while ((gt = *gtnp) != NULL) {
+ if (gt->gt_mcastgrp >= mcastgrp)
+ break;
+ gtnp = &gt->gt_next;
+ prev_gt = gt;
+ }
+ }
+
+ if (gt == NULL || gt->gt_mcastgrp != mcastgrp) {
+ gt = (struct gtable *)malloc(sizeof(struct gtable));
+ if (gt == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+
+ gt->gt_mcastgrp = mcastgrp;
+ gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ time(&gt->gt_ctime);
+ gt->gt_prsent_timer = 0;
+ gt->gt_grftsnt = 0;
+ gt->gt_srctbl = NULL;
+ gt->gt_pruntbl = NULL;
+ gt->gt_route = r;
+ gt->gt_rexmit_timer = 0;
+ NBRM_CLRALL(gt->gt_prunes);
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
+#ifdef RSRR
+ gt->gt_rsrr_cache = NULL;
+#endif
+
+ /* Calculate forwarding vifs */
+ determine_forwvifs(gt);
+
+ /* update ttls */
+ prun_add_ttls(gt);
+
+ gt->gt_next = *gtnp;
+ *gtnp = gt;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt;
+ gt->gt_prev = prev_gt;
+
+ if (r) {
+ if (find_src_grp(r->rt_origin, r->rt_originmask, gt->gt_mcastgrp)) {
+ struct gtable *g;
+
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ log(LOG_WARNING, 0, "Entry for (%s %s) (rt:%x) exists (rt:%x)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ r, g->gt_route);
+ } else {
+ if (gtp) {
+ gt->gt_gnext = gtp->gt_gnext;
+ gt->gt_gprev = gtp;
+ gtp->gt_gnext = gt;
+ } else {
+ gt->gt_gnext = kernel_table;
+ gt->gt_gprev = NULL;
+ kernel_table = gt;
+ }
+ if (gt->gt_gnext)
+ gt->gt_gnext->gt_gprev = gt;
+ }
+ } else {
+ gt->gt_gnext = gt->gt_gprev = NULL;
+ }
+ }
+
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ if (ntohl(st->st_origin) >= ntohl(origin))
+ break;
+ stnp = &st->st_next;
+ }
+
+ if (st == NULL || st->st_origin != origin) {
+ st = (struct stable *)malloc(sizeof(struct stable));
+ if (st == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+
+ st->st_origin = origin;
+ st->st_pktcnt = 0;
+ st->st_savpkt = 0;
+ time(&st->st_ctime);
+ st->st_next = *stnp;
+ *stnp = st;
+ } else {
+ if (st->st_ctime == 0) {
+ /* An old source which we're keeping around for statistics */
+ time(&st->st_ctime);
+ } else {
+#ifdef DEBUG_MFC
+ md_log(MD_DUPE, origin, mcastgrp);
+#endif
+ /* Ignore kernel->mrouted retransmissions */
+ if (time(0) - st->st_ctime > 5)
+ log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(mcastgrp, s2));
+ k_add_rg(origin, gt);
+ return;
+ }
+ }
+
+ kroutes++;
+ k_add_rg(origin, gt);
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "add cache entry (%s %s) gm:%x, parent-vif:%d",
+ inet_fmt(origin, s1),
+ inet_fmt(mcastgrp, s2),
+ gt->gt_grpmems, r ? r->rt_parent : -1);
+
+ /*
+ * If there are no downstream routers that want traffic for
+ * this group, send (or retransmit) a prune upstream.
+ */
+ if (VIFM_ISEMPTY(gt->gt_grpmems))
+ send_prune(gt);
+}
+
+/*
+ * A router has gone down. Remove prune state pertinent to that router.
+ */
+void
+reset_neighbor_state(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt, **ptnp;
+ struct stable *st;
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ r = g->gt_route;
+
+ /*
+ * If neighbor was the parent, remove the prune sent state
+ * and all of the source cache info so that prunes get
+ * regenerated.
+ */
+ if (vifi == r->rt_parent) {
+ if (addr == r->rt_gateway) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "reset_neighbor_state parent reset (%s %s)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
+
+ g->gt_prsent_timer = 0;
+ g->gt_grftsnt = 0;
+ while ((st = g->gt_srctbl) != NULL) {
+ g->gt_srctbl = st->st_next;
+ if (st->st_ctime != 0) {
+ k_del_rg(st->st_origin, g);
+ kroutes--;
+ }
+ free(st);
+ }
+ }
+ } else {
+ /*
+ * Remove any prunes that this router has sent us.
+ */
+ ptnp = &g->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if (pt->pt_vifi == vifi && pt->pt_router == addr) {
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
+ *ptnp = pt->pt_next;
+ free(pt);
+ } else
+ ptnp = &pt->pt_next;
+ }
+
+ /*
+ * And see if we want to forward again.
+ */
+ if (!VIFM_ISSET(vifi, g->gt_grpmems)) {
+ GET_MEMBERSHIP(g, vifi);
+ APPLY_SCOPE(g);
+ prun_add_ttls(g);
+
+ /* Update kernel state */
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+
+ /*
+ * If removing this prune causes us to start forwarding
+ * (e.g. the neighbor rebooted), and we sent a prune upstream,
+ * send a graft to cancel the prune.
+ */
+ if (!VIFM_ISEMPTY(g->gt_grpmems) && g->gt_prsent_timer)
+ send_graft(g);
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "reset neighbor state (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+ }
+ }
+ }
+}
+
+/*
+ * Delete table entry from the kernel
+ * del_flag determines how many entries to delete
+ */
+void
+del_table_entry(r, mcastgrp, del_flag)
+ struct rtentry *r;
+ u_int32 mcastgrp;
+ u_int del_flag;
+{
+ struct gtable *g, *prev_g;
+ struct stable *st, *prev_st;
+ struct ptable *pt, *prev_pt;
+
+ if (del_flag == DEL_ALL_ROUTES) {
+ g = r->rt_groups;
+ while (g) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
+ st = g->gt_srctbl;
+ while (st) {
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, g) < 0) {
+ log(LOG_WARNING, errno,
+ "del_table_entry trying to delete (%s, %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ prev_st = st;
+ st = st->st_next;
+ free(prev_st);
+ }
+ g->gt_srctbl = NULL;
+
+ pt = g->gt_pruntbl;
+ while (pt) {
+ prev_pt = pt;
+ pt = pt->pt_next;
+ free(prev_pt);
+ }
+ g->gt_pruntbl = NULL;
+
+ if (g->gt_gnext)
+ g->gt_gnext->gt_gprev = g->gt_gprev;
+ if (g->gt_gprev)
+ g->gt_gprev->gt_gnext = g->gt_gnext;
+ else
+ kernel_table = g->gt_gnext;
+
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,0);
+ rsrr_cache_clean(g);
+#endif /* RSRR */
+ if (g->gt_rexmit_timer)
+ timer_clearTimer(g->gt_rexmit_timer);
+
+ prev_g = g;
+ g = g->gt_next;
+ free(prev_g);
+ }
+ r->rt_groups = NULL;
+ }
+
+ /*
+ * Dummy routine - someday this may be needed, so it is just there
+ */
+ if (del_flag == DEL_RTE_GROUP) {
+ prev_g = (struct gtable *)&r->rt_groups;
+ for (g = r->rt_groups; g; g = g->gt_next) {
+ if (g->gt_mcastgrp == mcastgrp) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2));
+ st = g->gt_srctbl;
+ while (st) {
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, g) < 0) {
+ log(LOG_WARNING, errno,
+ "del_table_entry trying to delete (%s, %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ prev_st = st;
+ st = st->st_next;
+ free(prev_st);
+ }
+ g->gt_srctbl = NULL;
+
+ pt = g->gt_pruntbl;
+ while (pt) {
+ prev_pt = pt;
+ pt = pt->pt_next;
+ free(prev_pt);
+ }
+ g->gt_pruntbl = NULL;
+
+ if (g->gt_gnext)
+ g->gt_gnext->gt_gprev = g->gt_gprev;
+ if (g->gt_gprev)
+ g->gt_gprev->gt_gnext = g->gt_gnext;
+ else
+ kernel_table = g->gt_gnext;
+
+ if (prev_g != (struct gtable *)&r->rt_groups)
+ g->gt_next->gt_prev = prev_g;
+ else
+ g->gt_next->gt_prev = NULL;
+ prev_g->gt_next = g->gt_next;
+
+ if (g->gt_rexmit_timer)
+ timer_clearTimer(g->gt_rexmit_timer);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,0);
+ rsrr_cache_clean(g);
+#endif /* RSRR */
+ free(g);
+ g = prev_g;
+ } else {
+ prev_g = g;
+ }
+ }
+ }
+}
+
+/*
+ * update kernel table entry when a route entry changes
+ */
+void
+update_table_entry(r, old_parent_gw)
+ struct rtentry *r;
+ u_int32 old_parent_gw;
+{
+ struct gtable *g;
+ struct ptable *pt, **ptnp;
+
+ for (g = r->rt_groups; g; g = g->gt_next) {
+ ptnp = &g->gt_pruntbl;
+ /*
+ * Delete prune entries from non-children, or non-subordinates.
+ */
+ while ((pt = *ptnp)) {
+ if (!VIFM_ISSET(pt->pt_vifi, r->rt_children) ||
+ !NBRM_ISSET(pt->pt_index, r->rt_subordinates)) {
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "update_table_entry deleting prune for (%s %s) from %s on vif %d -%s%s",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3), pt->pt_vifi,
+ VIFM_ISSET(pt->pt_vifi, r->rt_children) ? "" : " not a child",
+ NBRM_ISSET(pt->pt_index, r->rt_subordinates) ? "" : " not a subordinate");
+
+ if (!NBRM_ISSET(pt->pt_index, g->gt_prunes)) {
+ log(LOG_WARNING, 0,
+ "gt_prunes lost track of (%s %s) from %s on vif %d",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3), pt->pt_vifi);
+ }
+
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
+ *ptnp = pt->pt_next;
+ free(pt);
+ continue;
+ }
+ ptnp = &((*ptnp)->pt_next);
+ }
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "updating cache entries (%s %s) old gm:%x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ g->gt_grpmems);
+
+ /*
+ * Forget about a prune or graft that we sent previously if we
+ * have a new parent router (since the new parent router will
+ * know nothing about what I sent to the previous parent). The
+ * old parent will forget any prune state it is keeping for us.
+ */
+ if (old_parent_gw != r->rt_gateway) {
+ g->gt_prsent_timer = 0;
+ g->gt_grftsnt = 0;
+ }
+
+ /* Recalculate membership */
+ determine_forwvifs(g);
+ /* send a prune or graft if needed. */
+ send_prune_or_graft(g);
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "updating cache entries (%s %s) new gm:%x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2),
+ g->gt_grpmems);
+
+ /* update ttls and add entry into kernel */
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+}
+
+/*
+ * set the forwarding flag for all mcastgrps on this vifi
+ */
+void
+update_lclgrp(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ struct rtentry *r;
+ struct gtable *g;
+
+ IF_DEBUG(DEBUG_MEMBER)
+ log(LOG_DEBUG, 0, "group %s joined on vif %d",
+ inet_fmt(mcastgrp, s1), vifi);
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ r = g->gt_route;
+ if (g->gt_mcastgrp == mcastgrp &&
+ VIFM_ISSET(vifi, r->rt_children)) {
+
+ VIFM_SET(vifi, g->gt_grpmems);
+ APPLY_SCOPE(g);
+ if (VIFM_ISEMPTY(g->gt_grpmems))
+ continue;
+
+ prun_add_ttls(g);
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+ }
+}
+
+/*
+ * reset forwarding flag for all mcastgrps on this vifi
+ */
+void
+delete_lclgrp(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ struct gtable *g;
+
+ IF_DEBUG(DEBUG_MEMBER)
+ log(LOG_DEBUG, 0, "group %s left on vif %d",
+ inet_fmt(mcastgrp, s1), vifi);
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, g->gt_grpmems)) {
+ if (g->gt_route == NULL ||
+ SUBS_ARE_PRUNED(g->gt_route->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, g->gt_prunes)) {
+ VIFM_CLR(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x",
+ RT_FMT(g->gt_route, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+
+ /*
+ * If there are no more members of this particular group,
+ * send prune upstream
+ */
+ if (VIFM_ISEMPTY(g->gt_grpmems) && g->gt_route->rt_gateway)
+ send_prune(g);
+ }
+ }
+ }
+}
+
+/*
+ * Takes the prune message received and then strips it to
+ * determine the (src, grp) pair to be pruned.
+ *
+ * Adds the router to the (src, grp) entry then.
+ *
+ * Determines if further packets have to be sent down that vif
+ *
+ * Determines if a corresponding prune message has to be generated
+ */
+void
+accept_prune(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+{
+ u_int32 prun_src;
+ u_int32 prun_grp;
+ u_int32 prun_tmr;
+ vifi_t vifi;
+ int i;
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring prune report from non-neighbor %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ /* Check if enough data is present */
+ if (datalen < 12)
+ {
+ log(LOG_WARNING, 0,
+ "non-decipherable prune from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ for (i = 0; i< 4; i++)
+ ((char *)&prun_src)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&prun_grp)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&prun_tmr)[i] = *p++;
+ prun_tmr = ntohl(prun_tmr);
+
+ if (prun_tmr <= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "ignoring prune from %s on vif %d for (%s %s)/%d because its lifetime is too short",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr);
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s on vif %d prunes (%s %s)/%d",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(prun_src, s2), inet_fmt(prun_grp, s3), prun_tmr);
+
+ /*
+ * Find the subnet for the prune
+ */
+ if (find_src_grp(prun_src, 0, prun_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ r = g->gt_route;
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "found grp state, (%s %s), metric is %d, children are %x, subords are %08x%08x",
+ RT_FMT(r, s1), inet_fmt(g->gt_mcastgrp, s2), r->rt_metric,
+ r->rt_children, r->rt_subordinates.hi, r->rt_subordinates.lo);
+ if (!VIFM_ISSET(vifi, r->rt_children)) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s) (dominant on vif %d is %s)",
+ inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3), vifi,
+ inet_fmt(r->rt_dominants[vifi], s4));
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ return;
+ }
+ if (VIFM_ISSET(vifi, g->gt_scope)) {
+ log(LOG_WARNING, 0, "prune received from %s on scoped grp (%s %s)",
+ inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3));
+ return;
+ }
+ if ((pt = find_prune_entry(src, g->gt_pruntbl)) != NULL) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s %d from %s for (%s %s)/%d %s %d %s %x",
+ "duplicate prune received on vif",
+ vifi, inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3), prun_tmr,
+ "old timer:", pt->pt_timer, "cur gm:", g->gt_grpmems);
+ pt->pt_timer = prun_tmr;
+ } else {
+ struct listaddr *n = neighbor_info(vifi, src);
+
+ if (!n) {
+ log(LOG_WARNING, 0, "Prune from non-neighbor %s on vif %d!?",
+ inet_fmt(src, s1), vifi);
+ return;
+ }
+
+ /* allocate space for the prune structure */
+ pt = (struct ptable *)(malloc(sizeof(struct ptable)));
+ if (pt == NULL)
+ log(LOG_ERR, 0, "pt: ran out of memory");
+
+ pt->pt_vifi = vifi;
+ pt->pt_router = src;
+ pt->pt_timer = prun_tmr;
+
+ pt->pt_next = g->gt_pruntbl;
+ g->gt_pruntbl = pt;
+
+ if (n) {
+ pt->pt_index = n->al_index;
+ NBRM_SET(n->al_index, g->gt_prunes);
+ }
+ }
+
+ /*
+ * check if any more packets need to be sent on the
+ * vif which sent this message
+ */
+ if (SUBS_ARE_PRUNED(r->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, g->gt_prunes) &&
+ !grplst_mem(vifi, prun_grp)) {
+ nbrbitmap_t tmp;
+
+ VIFM_CLR(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "vifnbrs=0x%08x%08x, subord=0x%08x%08x prunes=0x%08x%08x",
+ uvifs[vifi].uv_nbrmap.hi,uvifs[vifi].uv_nbrmap.lo,
+ r->rt_subordinates.hi, r->rt_subordinates.lo,
+ g->gt_prunes.hi, g->gt_prunes.lo);
+ /* XXX debugging */
+ NBRM_COPY(r->rt_subordinates, tmp);
+ NBRM_MASK(tmp, uvifs[vifi].uv_nbrmap);
+ if (!NBRM_ISSETALLMASK(g->gt_prunes, tmp))
+ log(LOG_WARNING, 0, "subordinate error");
+ /* XXX end debugging */
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), vifi, g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+
+ /*
+ * check if all the child routers have expressed no interest
+ * in this group and if this group does not exist in the
+ * interface
+ * Send a prune message then upstream
+ */
+ if (VIFM_ISEMPTY(g->gt_grpmems) && r->rt_gateway) {
+ send_prune(g);
+ }
+ } else {
+ /*
+ * There is no kernel entry for this group. Therefore, we can
+ * simply ignore the prune, as we are not forwarding this traffic
+ * downstream.
+ */
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "%s (%s %s)/%d from %s",
+ "prune message received with no kernel entry for",
+ inet_fmt(prun_src, s1), inet_fmt(prun_grp, s2),
+ prun_tmr, inet_fmt(src, s3));
+ return;
+ }
+}
+
+/*
+ * Checks if this mcastgrp is present in the kernel table
+ * If so and if a prune was sent, it sends a graft upwards
+ */
+void
+chkgrp_graft(vifi, mcastgrp)
+ vifi_t vifi;
+ u_int32 mcastgrp;
+{
+ struct rtentry *r;
+ struct gtable *g;
+
+ for (g = kernel_table; g; g = g->gt_gnext) {
+ if (ntohl(mcastgrp) < ntohl(g->gt_mcastgrp))
+ break;
+
+ r = g->gt_route;
+ if (g->gt_mcastgrp == mcastgrp && VIFM_ISSET(vifi, r->rt_children))
+ if (g->gt_prsent_timer) {
+ VIFM_SET(vifi, g->gt_grpmems);
+
+ /*
+ * If the vif that was joined was a scoped vif,
+ * ignore it ; don't graft back
+ */
+ APPLY_SCOPE(g);
+ if (VIFM_ISEMPTY(g->gt_grpmems))
+ continue;
+
+ /* send graft upwards */
+ send_graft(g);
+
+ /* update cache timer*/
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ }
+ }
+}
+
+/* determine the multicast group and src
+ *
+ * if it does, then determine if a prune was sent
+ * upstream.
+ * if prune sent upstream, send graft upstream and send
+ * ack downstream.
+ *
+ * if no prune sent upstream, change the forwarding bit
+ * for this interface and send ack downstream.
+ *
+ * if no entry exists for this group send ack downstream.
+ */
+void
+accept_graft(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+{
+ vifi_t vifi;
+ u_int32 graft_src;
+ u_int32 graft_grp;
+ int i;
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt, **ptnp;
+
+ if (datalen < 8) {
+ log(LOG_WARNING, 0,
+ "received non-decipherable graft from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ for (i = 0; i< 4; i++)
+ ((char *)&graft_src)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&graft_grp)[i] = *p++;
+
+ vifi = find_vif(src, dst);
+ send_graft_ack(dst, src, graft_src, graft_grp, vifi);
+
+ if (vifi == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft for (%s %s) from non-neighbor %s",
+ inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3),
+ inet_fmt(src, s1));
+ return;
+ }
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s on vif %d grafts (%s %s)",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(graft_src, s2), inet_fmt(graft_grp, s3));
+
+ /*
+ * Find the subnet for the graft
+ */
+ if (find_src_grp(graft_src, 0, graft_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ r = g->gt_route;
+
+ if (VIFM_ISSET(vifi, g->gt_scope)) {
+ log(LOG_WARNING, 0, "graft received from %s on scoped grp (%s %s)",
+ inet_fmt(src, s1), inet_fmt(graft_src, s2),
+ inet_fmt(graft_grp, s3));
+ return;
+ }
+
+ ptnp = &g->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if ((pt->pt_vifi == vifi) && (pt->pt_router == src)) {
+ NBRM_CLR(pt->pt_index, g->gt_prunes);
+ *ptnp = pt->pt_next;
+ free(pt);
+
+ VIFM_SET(vifi, g->gt_grpmems);
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x",
+ RT_FMT(r, s1),
+ inet_fmt(g->gt_mcastgrp, s2), g->gt_grpmems);
+
+ prun_add_ttls(g);
+ update_kernel(g);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(g,1);
+#endif /* RSRR */
+ break;
+ } else {
+ ptnp = &pt->pt_next;
+ }
+ }
+
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+
+ if (g->gt_prsent_timer)
+ /* send graft upwards */
+ send_graft(g);
+ } else {
+ /*
+ * We have no state for the source and group in question.
+ * This is fine, since we know that we have no prune state, and
+ * grafts are requests to remove prune state.
+ */
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s (%s %s) from %s",
+ "graft received with no kernel entry for",
+ inet_fmt(graft_src, s1), inet_fmt(graft_grp, s2),
+ inet_fmt(src, s3));
+ return;
+ }
+}
+
+/*
+ * find out which group is involved first of all
+ * then determine if a graft was sent.
+ * if no graft sent, ignore the message
+ * if graft was sent and the ack is from the right
+ * source, remove the graft timer so that we don't
+ * have send a graft again
+ */
+void
+accept_g_ack(src, dst, p, datalen)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+{
+ struct gtable *g;
+ vifi_t vifi;
+ u_int32 grft_src;
+ u_int32 grft_grp;
+ int i;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft ack from non-neighbor %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ if (datalen < 0 || datalen > 8) {
+ log(LOG_WARNING, 0,
+ "received non-decipherable graft ack from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ for (i = 0; i< 4; i++)
+ ((char *)&grft_src)[i] = *p++;
+ for (i = 0; i< 4; i++)
+ ((char *)&grft_grp)[i] = *p++;
+
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "%s on vif %d acks graft (%s, %s)",
+ inet_fmt(src, s1), vifi,
+ inet_fmt(grft_src, s2), inet_fmt(grft_grp, s3));
+
+ /*
+ * Find the subnet for the graft ack
+ */
+ if (find_src_grp(grft_src, 0, grft_grp)) {
+ g = gtp ? gtp->gt_gnext : kernel_table;
+ g->gt_grftsnt = 0;
+ } else {
+ log(LOG_WARNING, 0, "%s (%s, %s) from %s",
+ "rcvd graft ack with no kernel entry for",
+ inet_fmt(grft_src, s1), inet_fmt(grft_grp, s2),
+ inet_fmt(src, s3));
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ return;
+ }
+}
+
+
+/*
+ * free all prune entries and kernel routes
+ * normally, this should inform the kernel that all of its routes
+ * are going away, but this is only called by restart(), which is
+ * about to call MRT_DONE which does that anyway.
+ */
+void
+free_all_prunes()
+{
+ register struct rtentry *r;
+ register struct gtable *g, *prev_g;
+ register struct stable *s, *prev_s;
+ register struct ptable *p, *prev_p;
+
+ for (r = routing_table; r; r = r->rt_next) {
+ g = r->rt_groups;
+ while (g) {
+ s = g->gt_srctbl;
+ while (s) {
+ prev_s = s;
+ s = s->st_next;
+ free(prev_s);
+ }
+
+ p = g->gt_pruntbl;
+ while (p) {
+ prev_p = p;
+ p = p->pt_next;
+ free(prev_p);
+ }
+
+ prev_g = g;
+ g = g->gt_next;
+ if (prev_g->gt_rexmit_timer)
+ timer_clearTimer(prev_g->gt_rexmit_timer);
+ free(prev_g);
+ }
+ r->rt_groups = NULL;
+ }
+ kernel_table = NULL;
+
+ g = kernel_no_route;
+ while (g) {
+ if (g->gt_srctbl)
+ free(g->gt_srctbl);
+
+ prev_g = g;
+ g = g->gt_next;
+ if (prev_g->gt_rexmit_timer)
+ timer_clearTimer(prev_g->gt_rexmit_timer);
+ free(prev_g);
+ }
+ kernel_no_route = NULL;
+}
+
+/*
+ * When a new route is created, search
+ * a) The less-specific part of the routing table
+ * b) The route-less kernel table
+ * for sources that the new route might want to handle.
+ *
+ * "Inheriting" these sources might be cleanest, but simply deleting
+ * them is easier, and letting the kernel re-request them.
+ */
+void
+steal_sources(rt)
+ struct rtentry *rt;
+{
+ register struct rtentry *rp;
+ register struct gtable *gt, **gtnp;
+ register struct stable *st, **stnp;
+
+ for (rp = rt->rt_next; rp; rp = rp->rt_next) {
+ if (rp->rt_groups == NULL)
+ continue;
+ if ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "Route for %s stealing sources from %s",
+ RT_FMT(rt, s1), RT_FMT(rp, s2));
+ for (gt = rp->rt_groups; gt; gt = gt->gt_next) {
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ if ((st->st_origin & rt->rt_originmask) == rt->rt_origin) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ RT_FMT(rt, s1),
+ inet_fmt(st->st_origin, s3),
+ inet_fmt(gt->gt_mcastgrp, s4),
+ RT_FMT(rp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s, %s)",
+ "steal_sources trying to delete",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ *stnp = st->st_next;
+ free(st);
+ } else {
+ stnp = &st->st_next;
+ }
+ }
+ }
+ }
+ }
+
+ gtnp = &kernel_no_route;
+ while ((gt = *gtnp) != NULL) {
+ if (gt->gt_srctbl && ((gt->gt_srctbl->st_origin & rt->rt_originmask)
+ == rt->rt_origin)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ RT_FMT(rt, s1),
+ inet_fmt(gt->gt_srctbl->st_origin, s3),
+ inet_fmt(gt->gt_mcastgrp, s4),
+ "no_route table");
+ if (gt->gt_srctbl->st_ctime != 0) {
+ if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "steal_sources trying to delete",
+ inet_fmt(gt->gt_srctbl->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ free(gt->gt_srctbl);
+ *gtnp = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+ free(gt);
+ } else {
+ gtnp = &gt->gt_next;
+ }
+ }
+}
+
+/*
+ * Advance the timers on all the cache entries.
+ * If there are any entries whose timers have expired,
+ * remove these entries from the kernel cache.
+ */
+void
+age_table_entry()
+{
+ struct rtentry *r;
+ struct gtable *gt, **gtnptr;
+ struct stable *st, **stnp;
+ struct ptable *pt, **ptnp;
+ struct sioc_sg_req sg_req;
+
+ IF_DEBUG(DEBUG_PRUNE|DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "aging forwarding cache entries");
+
+ gtnptr = &kernel_table;
+ while ((gt = *gtnptr) != NULL) {
+ vifi_t i; /* XXX Debugging */
+ int fixit = 0; /* XXX Debugging */
+
+ r = gt->gt_route;
+
+ /* XXX Debugging... */
+ for (i = 0; i < numvifs; i++) {
+ /*
+ * If we're not sending on this vif,
+ * And this group isn't scoped on this vif,
+ * And I'm the parent for this route on this vif,
+ * And there are subordinates on this vif,
+ * And all of the subordinates haven't pruned,
+ * YELL LOUDLY
+ * and remember to fix it up later
+ */
+ if (!VIFM_ISSET(i, gt->gt_grpmems) &&
+ !VIFM_ISSET(i, gt->gt_scope) &&
+ VIFM_ISSET(i, r->rt_children) &&
+ NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates) &&
+ !SUBS_ARE_PRUNED(r->rt_subordinates, uvifs[i].uv_nbrmap, gt->gt_prunes)) {
+ log(LOG_WARNING, 0, "(%s %s) is blackholing on vif %d",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), i);
+ fixit = 1;
+ }
+ }
+ if (fixit) {
+ log(LOG_WARNING, 0, "fixing membership for (%s %s) gm:%x",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems);
+ determine_forwvifs(gt);
+ send_prune_or_graft(gt);
+ log(LOG_WARNING, 0, "fixed membership for (%s %s) gm:%x",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems);
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ }
+ /*DEBUG2*/
+ /* If there are group members,
+ * and there are recent sources,
+ * and we have a route,
+ * and it's not directly connected,
+ * and we haven't sent a prune,
+ * if there are any cache entries in the kernel
+ * [if there aren't we're probably waiting to rexmit],
+ * YELL LOUDLY
+ * and send a prune
+ */
+ if (VIFM_ISEMPTY(gt->gt_grpmems) && gt->gt_srctbl && r && r->rt_gateway && gt->gt_prsent_timer == 0) {
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (st->st_ctime != 0)
+ break;
+ if (st != NULL) {
+ log(LOG_WARNING, 0, "grpmems for (%s %s) is empty but no prune state!", RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ send_prune_or_graft(gt);
+#ifdef RINGBUFFER
+ printringbuf();
+#endif
+ }
+ }
+ /* XXX ...Debugging */
+
+ /* advance the timer for the kernel entry */
+ gt->gt_timer -= TIMER_INTERVAL;
+
+ /* decrement prune timer if need be */
+ if (gt->gt_prsent_timer > 0) {
+ gt->gt_prsent_timer -= TIMER_INTERVAL;
+ if (gt->gt_prsent_timer <= 0) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "upstream prune tmo (%s %s)",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ gt->gt_prsent_timer = -1;
+ /* Reset the prune retransmission timer to its initial value */
+ gt->gt_prune_rexmit = PRUNE_REXMIT_VAL;
+ }
+ }
+
+ /* retransmit graft with exponential backoff */
+ if (gt->gt_grftsnt) {
+ register int y;
+
+ y = ++gt->gt_grftsnt;
+ while (y && !(y & 1))
+ y >>= 1;
+ if (y == 1)
+ send_graft(gt);
+ }
+
+ /*
+ * Age prunes
+ *
+ * If a prune expires, forward again on that vif.
+ */
+ ptnp = &gt->gt_pruntbl;
+ while ((pt = *ptnp) != NULL) {
+ if ((pt->pt_timer -= TIMER_INTERVAL) <= 0) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3),
+ pt->pt_vifi);
+ if (gt->gt_prsent_timer > 0) {
+ log(LOG_WARNING, 0, "prune (%s %s) from %s on vif %d expires with %d left on prsent timer",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3),
+ pt->pt_vifi, gt->gt_prsent_timer);
+ /* Send a graft to heal the tree. */
+ send_graft(gt);
+ }
+
+ NBRM_CLR(pt->pt_index, gt->gt_prunes);
+ expire_prune(pt->pt_vifi, gt);
+
+ /* remove the router's prune entry and await new one */
+ *ptnp = pt->pt_next;
+ free(pt);
+ } else {
+ ptnp = &pt->pt_next;
+ }
+ }
+
+ /*
+ * If the cache entry has expired, delete source table entries for
+ * silent sources. If there are no source entries left, and there
+ * are no downstream prunes, then the entry is deleted.
+ * Otherwise, the cache entry's timer is refreshed.
+ */
+ if (gt->gt_timer <= 0) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "(%s %s) timed out, checking for traffic",
+ RT_FMT(gt->gt_route, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ /* Check for traffic before deleting source entries */
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ /*
+ * Source entries with no ctime are not actually in the
+ * kernel; they have been removed by rexmit_prune() so
+ * are safe to remove from the list at this point.
+ */
+ if (st->st_ctime) {
+ sg_req.src.s_addr = st->st_origin;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "age_table_entry: SIOCGETSGCNT failing for",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ /* Make sure it gets deleted below */
+ sg_req.pktcnt = st->st_pktcnt;
+ }
+ } else {
+ sg_req.pktcnt = st->st_pktcnt;
+ }
+ if (sg_req.pktcnt == st->st_pktcnt) {
+ *stnp = st->st_next;
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "age_table_entry deleting (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ if (st->st_ctime != 0) {
+ if (k_del_rg(st->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno,
+ "age_table_entry trying to delete (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ free(st);
+ } else {
+ st->st_pktcnt = sg_req.pktcnt;
+ stnp = &st->st_next;
+ }
+ }
+
+ /*
+ * Retain the group entry if we have downstream prunes or if
+ * there is at least one source in the list that still has
+ * traffic, or if our upstream prune timer or graft
+ * retransmission timer is running.
+ */
+ if (gt->gt_pruntbl != NULL || gt->gt_srctbl != NULL ||
+ gt->gt_prsent_timer > 0 || gt->gt_grftsnt > 0) {
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "refresh lifetim of cache entry %s%s%s%s(%s, %s)",
+ gt->gt_pruntbl ? "(dstrm prunes) " : "",
+ gt->gt_srctbl ? "(trfc flow) " : "",
+ gt->gt_prsent_timer > 0 ? "(upstrm prune) " : "",
+ gt->gt_grftsnt > 0 ? "(grft rexmit) " : "",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ if (gt->gt_prsent_timer == -1) {
+ /*
+ * The upstream prune timed out. Remove any kernel
+ * state.
+ */
+ gt->gt_prsent_timer = 0;
+ if (gt->gt_pruntbl) {
+ log(LOG_WARNING, 0, "upstream prune for (%s %s) expires with downstream prunes active",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ remove_sources(gt);
+ }
+ gtnptr = &gt->gt_gnext;
+ continue;
+ }
+
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "timeout cache entry (%s, %s)",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+
+ if (gt->gt_prev)
+ gt->gt_prev->gt_next = gt->gt_next;
+ else
+ gt->gt_route->rt_groups = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+
+ if (gt->gt_gprev) {
+ gt->gt_gprev->gt_gnext = gt->gt_gnext;
+ gtnptr = &gt->gt_gprev->gt_gnext;
+ } else {
+ kernel_table = gt->gt_gnext;
+ gtnptr = &kernel_table;
+ }
+ if (gt->gt_gnext)
+ gt->gt_gnext->gt_gprev = gt->gt_gprev;
+
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(gt,0);
+ rsrr_cache_clean(gt);
+#endif /* RSRR */
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
+ free((char *)gt);
+ } else {
+ if (gt->gt_prsent_timer == -1) {
+ /*
+ * The upstream prune timed out. Remove any kernel
+ * state.
+ */
+ gt->gt_prsent_timer = 0;
+ if (gt->gt_pruntbl) {
+ log(LOG_WARNING, 0, "upstream prune for (%s %s) expires with downstream prunes active",
+ RT_FMT(r, s1), inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ remove_sources(gt);
+ }
+ gtnptr = &gt->gt_gnext;
+ }
+ }
+
+ /*
+ * When traversing the no_route table, the decision is much easier.
+ * Just delete it if it has timed out.
+ */
+ gtnptr = &kernel_no_route;
+ while ((gt = *gtnptr) != NULL) {
+ /* advance the timer for the kernel entry */
+ gt->gt_timer -= TIMER_INTERVAL;
+
+ if (gt->gt_timer < 0) {
+ if (gt->gt_srctbl) {
+ if (gt->gt_srctbl->st_ctime != 0) {
+ if (k_del_rg(gt->gt_srctbl->st_origin, gt) < 0) {
+ log(LOG_WARNING, errno, "%s (%s %s)",
+ "age_table_entry trying to delete no-route",
+ inet_fmt(gt->gt_srctbl->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ }
+ kroutes--;
+ }
+ free(gt->gt_srctbl);
+ }
+ *gtnptr = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+
+ if (gt->gt_rexmit_timer)
+ timer_clearTimer(gt->gt_rexmit_timer);
+
+ free((char *)gt);
+ } else {
+ gtnptr = &gt->gt_next;
+ }
+ }
+}
+
+/*
+ * Modify the kernel to forward packets when one or multiple prunes that
+ * were received on the vif given by vifi, for the group given by gt,
+ * have expired.
+ */
+static void
+expire_prune(vifi, gt)
+ vifi_t vifi;
+ struct gtable *gt;
+{
+ /*
+ * No need to send a graft, any prunes that we sent
+ * will expire before any prunes that we have received.
+ * However, in the case that we did make a mistake,
+ * send a graft to compensate.
+ */
+ if (gt->gt_prsent_timer >= MIN_PRUNE_LIFE) {
+ IF_DEBUG(DEBUG_PRUNE)
+ log(LOG_DEBUG, 0, "prune expired with %d left on %s",
+ gt->gt_prsent_timer, "prsent_timer");
+ gt->gt_prsent_timer = 0;
+ send_graft(gt);
+ }
+
+ /* modify the kernel entry to forward packets */
+ if (!VIFM_ISSET(vifi, gt->gt_grpmems)) {
+ struct rtentry *rt = gt->gt_route;
+ VIFM_SET(vifi, gt->gt_grpmems);
+ IF_DEBUG(DEBUG_CACHE)
+ log(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d",
+ RT_FMT(rt, s1),
+ inet_fmt(gt->gt_mcastgrp, s2), gt->gt_grpmems, vifi);
+
+ prun_add_ttls(gt);
+ update_kernel(gt);
+#ifdef RSRR
+ /* Send route change notification to reservation protocol. */
+ rsrr_cache_send(gt,1);
+#endif /* RSRR */
+ }
+}
+
+/*
+ * Print the contents of the cache table on file 'fp2'.
+ */
+void
+dump_cache(fp2)
+ FILE *fp2;
+{
+ register struct rtentry *r;
+ register struct gtable *gt;
+ register struct stable *st;
+ register struct ptable *pt;
+ register vifi_t i;
+ char c;
+ register time_t thyme = time(0);
+
+ fprintf(fp2,
+ "Multicast Routing Cache Table (%d entries)\n%s", kroutes,
+ " Origin Mcast-group CTmr Age Ptmr Rx IVif Forwvifs\n");
+ fprintf(fp2,
+ "<(prunesrc:vif[idx]/tmr) prunebitmap\n%s",
+ ">Source Lifetime SavPkt Pkts Bytes RPFf\n");
+
+ for (gt = kernel_no_route; gt; gt = gt->gt_next) {
+ if (gt->gt_srctbl) {
+ fprintf(fp2, " %-18s %-15s %-8s %-8s - -1 (no route)\n",
+ inet_fmts(gt->gt_srctbl->st_origin, 0xffffffff, s1),
+ inet_fmt(gt->gt_mcastgrp, s2), scaletime(gt->gt_timer),
+ scaletime(thyme - gt->gt_ctime));
+ fprintf(fp2, ">%s\n", inet_fmt(gt->gt_srctbl->st_origin, s1));
+ }
+ }
+
+ for (gt = kernel_table; gt; gt = gt->gt_gnext) {
+ r = gt->gt_route;
+ fprintf(fp2, " %-18s %-15s",
+ RT_FMT(r, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+
+ fprintf(fp2, " %-8s", scaletime(gt->gt_timer));
+
+ fprintf(fp2, " %-8s %-8s ", scaletime(thyme - gt->gt_ctime),
+ gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) :
+ " -");
+
+ if (gt->gt_prune_rexmit) {
+ int i = gt->gt_prune_rexmit;
+ int n = 0;
+
+ while (i > PRUNE_REXMIT_VAL) {
+ n++;
+ i /= 2;
+ }
+ if (n == 0 && gt->gt_prsent_timer == 0)
+ fprintf(fp2, " -");
+ else
+ fprintf(fp2, "%2d", n);
+ } else {
+ fprintf(fp2, " -");
+ }
+
+ fprintf(fp2, " %2u%c%c", r->rt_parent,
+ gt->gt_prsent_timer ? 'P' :
+ gt->gt_grftsnt ? 'G' : ' ',
+ VIFM_ISSET(r->rt_parent, gt->gt_scope) ? 'B' : ' ');
+
+ for (i = 0; i < numvifs; ++i) {
+ if (VIFM_ISSET(i, gt->gt_grpmems))
+ fprintf(fp2, " %u ", i);
+ else if (VIFM_ISSET(i, r->rt_children) &&
+ NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ fprintf(fp2, " %u%c", i,
+ VIFM_ISSET(i, gt->gt_scope) ? 'b' :
+ SUBS_ARE_PRUNED(r->rt_subordinates,
+ uvifs[i].uv_nbrmap, gt->gt_prunes) ? 'p' : '!');
+ }
+ fprintf(fp2, "\n");
+ if (gt->gt_pruntbl) {
+ fprintf(fp2, "<");
+ c = '(';
+ for (pt = gt->gt_pruntbl; pt; pt = pt->pt_next) {
+ fprintf(fp2, "%c%s:%d[%d]/%d", c, inet_fmt(pt->pt_router, s1),
+ pt->pt_vifi, pt->pt_index, pt->pt_timer);
+ c = ',';
+ }
+ fprintf(fp2, ")");
+ fprintf(fp2, " 0x%08lx%08lx\n",/*XXX*/
+ gt->gt_prunes.hi, gt->gt_prunes.lo);
+ }
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ fprintf(fp2, ">%-18s %-8s %6ld", inet_fmt(st->st_origin, s1),
+ st->st_ctime ? scaletime(thyme - st->st_ctime) : "-",
+ st->st_savpkt);
+ if (st->st_ctime) {
+ struct sioc_sg_req sg_req;
+
+ sg_req.src.s_addr = st->st_origin;
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) < 0) {
+ log(LOG_WARNING, errno, "SIOCGETSGCNT on (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ } else {
+ fprintf(fp2, " %8ld %8ld %4ld", sg_req.pktcnt,
+ sg_req.bytecnt, sg_req.wrong_if);
+ }
+ }
+ fprintf(fp2, "\n");
+ }
+ }
+}
+
+/*
+ * Traceroute function which returns traceroute replies to the requesting
+ * router. Also forwards the request to downstream routers.
+ */
+void
+accept_mtrace(src, dst, group, data, no, datalen)
+ u_int32 src;
+ u_int32 dst;
+ u_int32 group;
+ char *data;
+ u_int no; /* promoted u_char */
+ int datalen;
+{
+ u_char type;
+ struct rtentry *rt;
+ struct gtable *gt;
+ struct tr_query *qry;
+ struct tr_resp *resp;
+ int vifi;
+ char *p;
+ int rcount;
+ int errcode = TR_NO_ERR;
+ int resptype;
+ struct timeval tp;
+ struct sioc_vif_req v_req;
+ struct sioc_sg_req sg_req;
+
+ /* Remember qid across invocations */
+ static u_int32 oqid = 0;
+
+ /* timestamp the request/response */
+ gettimeofday(&tp, 0);
+
+ /*
+ * Check if it is a query or a response
+ */
+ if (datalen == QLEN) {
+ type = QUERY;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Initial traceroute query rcvd from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ }
+ else if ((datalen - QLEN) % RLEN == 0) {
+ type = RESP;
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "In-transit traceroute query rcvd from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ if (IN_MULTICAST(ntohl(dst))) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Dropping multicast response");
+ return;
+ }
+ }
+ else {
+ log(LOG_WARNING, 0, "%s from %s to %s",
+ "Non decipherable traceroute request recieved",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ return;
+ }
+
+ qry = (struct tr_query *)data;
+
+ /*
+ * if it is a packet with all reports filled, drop it
+ */
+ if ((rcount = (datalen - QLEN)/RLEN) == no) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "packet with all reports filled in");
+ return;
+ }
+
+ IF_DEBUG(DEBUG_TRACE) {
+ log(LOG_DEBUG, 0, "s: %s g: %s d: %s ", inet_fmt(qry->tr_src, s1),
+ inet_fmt(group, s2), inet_fmt(qry->tr_dst, s3));
+ log(LOG_DEBUG, 0, "rttl: %d rd: %s", qry->tr_rttl,
+ inet_fmt(qry->tr_raddr, s1));
+ log(LOG_DEBUG, 0, "rcount:%d, qid:%06x", rcount, qry->tr_qid);
+ }
+
+ /* determine the routing table entry for this traceroute */
+ rt = determine_route(qry->tr_src);
+ IF_DEBUG(DEBUG_TRACE)
+ if (rt) {
+ log(LOG_DEBUG, 0, "rt parent vif: %d rtr: %s metric: %d",
+ rt->rt_parent, inet_fmt(rt->rt_gateway, s1), rt->rt_metric);
+ log(LOG_DEBUG, 0, "rt origin %s",
+ RT_FMT(rt, s1));
+ } else
+ log(LOG_DEBUG, 0, "...no route");
+
+ /*
+ * Query type packet - check if rte exists
+ * Check if the query destination is a vif connected to me.
+ * and if so, whether I should start response back
+ */
+ if (type == QUERY) {
+ if (oqid == qry->tr_qid) {
+ /*
+ * If the multicast router is a member of the group being
+ * queried, and the query is multicasted, then the router can
+ * recieve multiple copies of the same query. If we have already
+ * replied to this traceroute, just ignore it this time.
+ *
+ * This is not a total solution, but since if this fails you
+ * only get N copies, N <= the number of interfaces on the router,
+ * it is not fatal.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "ignoring duplicate traceroute packet");
+ return;
+ }
+
+ if (rt == NULL) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Mcast traceroute: no route entry %s",
+ inet_fmt(qry->tr_src, s1));
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ }
+ vifi = find_vif(qry->tr_dst, 0);
+
+ if (vifi == NO_VIF) {
+ /* The traceroute destination is not on one of my subnet vifs. */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Destination %s not an interface",
+ inet_fmt(qry->tr_dst, s1));
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ errcode = TR_WRONG_IF;
+ } else if (rt != NULL && !VIFM_ISSET(vifi, rt->rt_children)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s",
+ inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
+ if (IN_MULTICAST(ntohl(dst)))
+ return;
+ errcode = TR_WRONG_IF;
+ }
+ }
+ else {
+ /*
+ * determine which interface the packet came in on
+ * RESP packets travel hop-by-hop so this either traversed
+ * a tunnel or came from a directly attached mrouter.
+ */
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Wrong interface for packet");
+ errcode = TR_WRONG_IF;
+ }
+ }
+
+ /* Now that we've decided to send a response, save the qid */
+ oqid = qry->tr_qid;
+
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending traceroute response");
+
+ /* copy the packet to the sending buffer */
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ bcopy(data, p, datalen);
+
+ p += datalen;
+
+ /*
+ * If there is no room to insert our reply, coopt the previous hop
+ * error indication to relay this fact.
+ */
+ if (p + sizeof(struct tr_resp) > send_buf + RECV_BUF_SIZE) {
+ resp = (struct tr_resp *)p - 1;
+ resp->tr_rflags = TR_NO_SPACE;
+ rt = NULL;
+ goto sendit;
+ }
+
+ /*
+ * fill in initial response fields
+ */
+ resp = (struct tr_resp *)p;
+ bzero(resp, sizeof(struct tr_resp));
+ datalen += RLEN;
+
+ resp->tr_qarr = htonl(((tp.tv_sec + JAN_1970) << 16) +
+ ((tp.tv_usec << 10) / 15625));
+
+ resp->tr_rproto = PROTO_DVMRP;
+ resp->tr_outaddr = (vifi == NO_VIF) ? dst : uvifs[vifi].uv_lcl_addr;
+ resp->tr_fttl = (vifi == NO_VIF) ? 0 : uvifs[vifi].uv_threshold;
+ resp->tr_rflags = errcode;
+
+ /*
+ * obtain # of packets out on interface
+ */
+ v_req.vifi = vifi;
+ if (vifi != NO_VIF && ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ resp->tr_vifout = htonl(v_req.ocount);
+ else
+ resp->tr_vifout = 0xffffffff;
+
+ /*
+ * fill in scoping & pruning information
+ */
+ if (rt)
+ for (gt = rt->rt_groups; gt; gt = gt->gt_next) {
+ if (gt->gt_mcastgrp >= group)
+ break;
+ }
+ else
+ gt = NULL;
+
+ if (gt && gt->gt_mcastgrp == group) {
+ struct stable *st;
+
+ for (st = gt->gt_srctbl; st; st = st->st_next)
+ if (qry->tr_src == st->st_origin)
+ break;
+
+ sg_req.src.s_addr = qry->tr_src;
+ sg_req.grp.s_addr = group;
+ if (st && st->st_ctime != 0 &&
+ ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
+ resp->tr_pktcnt = htonl(sg_req.pktcnt + st->st_savpkt);
+ else
+ resp->tr_pktcnt = htonl(st ? st->st_savpkt : 0xffffffff);
+
+ if (VIFM_ISSET(vifi, gt->gt_scope))
+ resp->tr_rflags = TR_SCOPED;
+ else if (gt->gt_prsent_timer)
+ resp->tr_rflags = TR_PRUNED;
+ else if (!VIFM_ISSET(vifi, gt->gt_grpmems))
+ if (!NBRM_ISEMPTY(uvifs[vifi].uv_nbrmap) &&
+ SUBS_ARE_PRUNED(rt->rt_subordinates,
+ uvifs[vifi].uv_nbrmap, gt->gt_prunes))
+ resp->tr_rflags = TR_OPRUNED;
+ else
+ resp->tr_rflags = TR_NO_FWD;
+ } else {
+ if ((vifi != NO_VIF && scoped_addr(vifi, group)) ||
+ (rt && scoped_addr(rt->rt_parent, group)))
+ resp->tr_rflags = TR_SCOPED;
+ else if (rt && !VIFM_ISSET(vifi, rt->rt_children))
+ resp->tr_rflags = TR_NO_FWD;
+ }
+
+ /*
+ * if no rte exists, set NO_RTE error
+ */
+ if (rt == NULL) {
+ src = dst; /* the dst address of resp. pkt */
+ resp->tr_inaddr = 0;
+ resp->tr_rflags = TR_NO_RTE;
+ resp->tr_rmtaddr = 0;
+ } else {
+ /* get # of packets in on interface */
+ v_req.vifi = rt->rt_parent;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ resp->tr_vifin = htonl(v_req.icount);
+ else
+ resp->tr_vifin = 0xffffffff;
+
+ MASK_TO_VAL(rt->rt_originmask, resp->tr_smask);
+ src = uvifs[rt->rt_parent].uv_lcl_addr;
+ resp->tr_inaddr = src;
+ resp->tr_rmtaddr = rt->rt_gateway;
+ if (!VIFM_ISSET(vifi, rt->rt_children)) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Destination %s not on forwarding tree for src %s",
+ inet_fmt(qry->tr_dst, s1), inet_fmt(qry->tr_src, s2));
+ resp->tr_rflags = TR_WRONG_IF;
+ }
+ if (rt->rt_metric >= UNREACHABLE) {
+ resp->tr_rflags = TR_NO_RTE;
+ /* Hack to send reply directly */
+ rt = NULL;
+ }
+ }
+
+sendit:
+ /*
+ * if metric is 1 or no. of reports is 1, send response to requestor
+ * else send to upstream router. If the upstream router can't handle
+ * mtrace, set an error code and send to requestor anyway.
+ */
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "rcount:%d, no:%d", rcount, no);
+
+ if ((rcount + 1 == no) || (rt == NULL) || (rt->rt_metric == 1)) {
+ resptype = IGMP_MTRACE_RESP;
+ dst = qry->tr_raddr;
+ } else
+ if (!can_mtrace(rt->rt_parent, rt->rt_gateway)) {
+ dst = qry->tr_raddr;
+ resp->tr_rflags = TR_OLD_ROUTER;
+ resptype = IGMP_MTRACE_RESP;
+ } else {
+ dst = rt->rt_gateway;
+ resptype = IGMP_MTRACE;
+ }
+
+ if (IN_MULTICAST(ntohl(dst))) {
+ /*
+ * Send the reply on a known multicast capable vif.
+ * If we don't have one, we can't source any multicasts anyway.
+ */
+ if (phys_vif != -1) {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending reply to %s from %s",
+ inet_fmt(dst, s1), inet_fmt(uvifs[phys_vif].uv_lcl_addr, s2));
+ k_set_ttl(qry->tr_rttl);
+ send_igmp(uvifs[phys_vif].uv_lcl_addr, dst,
+ resptype, no, group,
+ datalen);
+ k_set_ttl(1);
+ } else
+ log(LOG_INFO, 0, "No enabled phyints -- %s",
+ "dropping traceroute reply");
+ } else {
+ IF_DEBUG(DEBUG_TRACE)
+ log(LOG_DEBUG, 0, "Sending %s to %s from %s",
+ resptype == IGMP_MTRACE_RESP ? "reply" : "request on",
+ inet_fmt(dst, s1), inet_fmt(src, s2));
+
+ send_igmp(src, dst,
+ resptype, no, group,
+ datalen);
+ }
+ return;
+}
diff --git a/usr.sbin/mrouted/prune.h b/usr.sbin/mrouted/prune.h
new file mode 100644
index 0000000..375fada
--- /dev/null
+++ b/usr.sbin/mrouted/prune.h
@@ -0,0 +1,152 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * prune.h,v 3.8.4.5 1998/02/27 22:45:43 fenner Exp
+ */
+
+/*
+ * Group table
+ *
+ * Each group entry is a member of two doubly-linked lists:
+ *
+ * a) A list hanging off of the routing table entry for this source (rt_groups)
+ * sorted by group address under the routing entry (gt_next, gt_prev)
+ * b) An independent list pointed to by kernel_table, which is a list of
+ * active source,group's (gt_gnext, gt_gprev).
+ *
+ */
+struct gtable {
+ struct gtable *gt_next; /* pointer to the next entry */
+ struct gtable *gt_prev; /* back pointer for linked list */
+ struct gtable *gt_gnext; /* fwd pointer for group list */
+ struct gtable *gt_gprev; /* rev pointer for group list */
+ u_int32 gt_mcastgrp; /* multicast group associated */
+ vifbitmap_t gt_scope; /* scoped interfaces */
+ u_char gt_ttls[MAXVIFS]; /* ttl vector for forwarding */
+ vifbitmap_t gt_grpmems; /* forw. vifs for src, grp */
+ int gt_prsent_timer; /* prune timer for this group */
+ int gt_timer; /* timer for this group entry */
+ time_t gt_ctime; /* time of entry creation */
+ u_char gt_grftsnt; /* graft sent/retransmit timer */
+ nbrbitmap_t gt_prunes; /* bitmap of neighbors who pruned */
+ struct stable *gt_srctbl; /* source table */
+ struct ptable *gt_pruntbl; /* prune table */
+ struct rtentry *gt_route; /* parent route */
+ int gt_rexmit_timer; /* timer for prune retransmission */
+ int gt_prune_rexmit; /* time til prune retransmission */
+#ifdef RSRR
+ struct rsrr_cache *gt_rsrr_cache; /* RSRR cache */
+#endif /* RSRR */
+};
+
+/*
+ * Source table
+ *
+ * When source-based prunes exist, there will be a struct ptable here as well.
+ */
+struct stable
+{
+ struct stable *st_next; /* pointer to the next entry */
+ u_int32 st_origin; /* host origin of multicasts */
+ u_long st_pktcnt; /* packet count for src-grp entry */
+ u_long st_savpkt; /* saved pkt cnt when no krnl entry */
+ time_t st_ctime; /* kernel entry creation time */
+};
+
+/*
+ * structure to store incoming prunes. Can hang off of either group or source.
+ */
+struct ptable
+{
+ struct ptable *pt_next; /* pointer to the next entry */
+ u_int32 pt_router; /* router that sent this prune */
+ vifi_t pt_vifi; /* vif prune received on */
+ int pt_index; /* neighbor index of router */
+ int pt_timer; /* timer for prune */
+};
+
+#define MIN_PRUNE_LIFE TIMER_INTERVAL /* min prune lifetime to bother with */
+
+/*
+ * The packet format for a traceroute request.
+ */
+struct tr_query {
+ u_int32 tr_src; /* traceroute source */
+ u_int32 tr_dst; /* traceroute destination */
+ u_int32 tr_raddr; /* traceroute response address */
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+ struct {
+ u_int qid : 24; /* traceroute query id */
+ u_int ttl : 8; /* traceroute response ttl */
+ } q;
+#else
+ struct {
+ u_int ttl : 8; /* traceroute response ttl */
+ u_int qid : 24; /* traceroute query id */
+ } q;
+#endif /* BYTE_ORDER */
+};
+
+#define tr_rttl q.ttl
+#define tr_qid q.qid
+
+/*
+ * Traceroute response format. A traceroute response has a tr_query at the
+ * beginning, followed by one tr_resp for each hop taken.
+ */
+struct tr_resp {
+ u_int32 tr_qarr; /* query arrival time */
+ u_int32 tr_inaddr; /* incoming interface address */
+ u_int32 tr_outaddr; /* outgoing interface address */
+ u_int32 tr_rmtaddr; /* parent address in source tree */
+ u_int32 tr_vifin; /* input packet count on interface */
+ u_int32 tr_vifout; /* output packet count on interface */
+ u_int32 tr_pktcnt; /* total incoming packets for src-grp */
+ u_char tr_rproto; /* routing protocol deployed on router */
+ u_char tr_fttl; /* ttl required to forward on outvif */
+ u_char tr_smask; /* subnet mask for src addr */
+ u_char tr_rflags; /* forwarding error codes */
+};
+
+/* defs within mtrace */
+#define QUERY 1
+#define RESP 2
+#define QLEN sizeof(struct tr_query)
+#define RLEN sizeof(struct tr_resp)
+
+/* fields for tr_rflags (forwarding error codes) */
+#define TR_NO_ERR 0
+#define TR_WRONG_IF 1
+#define TR_PRUNED 2
+#define TR_OPRUNED 3
+#define TR_SCOPED 4
+#define TR_NO_RTE 5
+#define TR_NO_FWD 7
+#define TR_NO_SPACE 0x81
+#define TR_OLD_ROUTER 0x82
+
+/* fields for tr_rproto (routing protocol) */
+#define PROTO_DVMRP 1
+#define PROTO_MOSPF 2
+#define PROTO_PIM 3
+#define PROTO_CBT 4
+
+#define MASK_TO_VAL(x, i) { \
+ u_int32 _x = ntohl(x); \
+ (i) = 1; \
+ while ((_x) <<= 1) \
+ (i)++; \
+ };
+
+#define VAL_TO_MASK(x, i) { \
+ x = i ? htonl(~((1 << (32 - (i))) - 1)) : 0; \
+ };
+
+#define NBR_VERS(n) (((n)->al_pv << 8) + (n)->al_mv)
diff --git a/usr.sbin/mrouted/route.c b/usr.sbin/mrouted/route.c
new file mode 100644
index 0000000..b269539
--- /dev/null
+++ b/usr.sbin/mrouted/route.c
@@ -0,0 +1,1475 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * route.c,v 3.8.4.41 1998/01/15 00:08:34 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * This define statement saves a lot of space later
+ */
+#define RT_ADDR (struct rtentry *)&routing_table
+
+/*
+ * Exported variables.
+ */
+int routes_changed; /* 1=>some routes have changed */
+int delay_change_reports; /* 1=>postpone change reports */
+
+
+/*
+ * The routing table is shared with prune.c , so must not be static.
+ */
+struct rtentry *routing_table; /* pointer to list of route entries */
+
+/*
+ * Private variables.
+ */
+static struct rtentry *rtp; /* pointer to a route entry */
+static struct rtentry *rt_end; /* pointer to last route entry */
+unsigned int nroutes; /* current number of route entries */
+
+/*
+ * Private functions.
+ */
+static int init_children_and_leaves __P((struct rtentry *r,
+ vifi_t parent, int first));
+static int find_route __P((u_int32 origin, u_int32 mask));
+static void create_route __P((u_int32 origin, u_int32 mask));
+static void discard_route __P((struct rtentry *prev_r));
+static int compare_rts __P((const void *rt1, const void *rt2));
+static int report_chunk __P((int, struct rtentry *start_rt, vifi_t vifi,
+ u_int32 dst));
+static void queue_blaster_report __P((vifi_t, u_int32, u_int32, char *,
+ int, u_int32));
+static void process_blaster_report __P((void *));
+
+#ifdef SNMP
+#include <sys/types.h>
+#include "snmp.h"
+
+/*
+ * Return pointer to a specific route entry. This must be a separate
+ * function from find_route() which modifies rtp.
+ */
+struct rtentry *
+snmp_find_route(src, mask)
+ register u_int32 src, mask;
+{
+ register struct rtentry *rt;
+
+ for (rt = routing_table; rt; rt = rt->rt_next) {
+ if (src == rt->rt_origin && mask == rt->rt_originmask)
+ return rt;
+ }
+ return NULL;
+}
+
+/*
+ * Find next route entry > specification
+ */
+int
+next_route(rtpp, src, mask)
+ struct rtentry **rtpp;
+ u_int32 src;
+ u_int32 mask;
+{
+ struct rtentry *rt, *rbest = NULL;
+
+ /* Among all entries > spec, find "lowest" one in order */
+ for (rt = routing_table; rt; rt=rt->rt_next) {
+ if ((ntohl(rt->rt_origin) > ntohl(src)
+ || (ntohl(rt->rt_origin) == ntohl(src)
+ && ntohl(rt->rt_originmask) > ntohl(mask)))
+ && (!rbest || (ntohl(rt->rt_origin) < ntohl(rbest->rt_origin))
+ || (ntohl(rt->rt_origin) == ntohl(rbest->rt_origin)
+ && ntohl(rt->rt_originmask) < ntohl(rbest->rt_originmask))))
+ rbest = rt;
+ }
+ (*rtpp) = rbest;
+ return (*rtpp)!=0;
+}
+
+/*
+ * Given a routing table entry, and a vifi, find the next vifi/entry
+ */
+int
+next_route_child(rtpp, src, mask, vifi)
+ struct rtentry **rtpp;
+ u_int32 src;
+ u_int32 mask;
+ vifi_t *vifi; /* vif at which to start looking */
+{
+ /* Get (S,M) entry */
+ if (!((*rtpp) = snmp_find_route(src,mask)))
+ if (!next_route(rtpp, src, mask))
+ return 0;
+
+ /* Continue until we get one with a valid next vif */
+ do {
+ for (; (*rtpp)->rt_children && *vifi<numvifs; (*vifi)++)
+ if (VIFM_ISSET(*vifi, (*rtpp)->rt_children))
+ return 1;
+ *vifi = 0;
+ } while( next_route(rtpp, (*rtpp)->rt_origin, (*rtpp)->rt_originmask) );
+
+ return 0;
+}
+#endif
+
+/*
+ * Initialize the routing table and associated variables.
+ */
+void
+init_routes()
+{
+ routing_table = NULL;
+ rt_end = RT_ADDR;
+ nroutes = 0;
+ routes_changed = FALSE;
+ delay_change_reports = FALSE;
+}
+
+
+/*
+ * Initialize the children bits for route 'r', along with the
+ * associated dominant and subordinate data structures.
+ * If first is set, initialize dominants, otherwise keep old
+ * dominants on non-parent interfaces.
+ * XXX Does this need a return value?
+ */
+static int
+init_children_and_leaves(r, parent, first)
+ register struct rtentry *r;
+ register vifi_t parent;
+ int first;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ vifbitmap_t old_children;
+ nbrbitmap_t old_subords;
+
+ VIFM_COPY(r->rt_children, old_children);
+ NBRM_COPY(r->rt_subordinates, old_subords);
+
+ VIFM_CLRALL(r->rt_children);
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (first || vifi == parent)
+ r->rt_dominants [vifi] = 0;
+ if (vifi == parent || uvifs[vifi].uv_flags & VIFF_NOFLOOD ||
+ AVOID_TRANSIT(vifi, r) || (!first && r->rt_dominants[vifi]))
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+
+ if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED)) &&
+ !(!first && r->rt_dominants[vifi])) {
+ VIFM_SET(vifi, r->rt_children);
+ }
+ }
+
+ return (!VIFM_SAME(r->rt_children, old_children) ||
+ !NBRM_SAME(r->rt_subordinates, old_subords));
+}
+
+
+/*
+ * A new vif has come up -- update the children bitmaps in all route
+ * entries to take that into account.
+ */
+void
+add_vif_to_routes(vifi)
+ register vifi_t vifi;
+{
+ register struct rtentry *r;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE &&
+ !VIFM_ISSET(vifi, r->rt_children)) {
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants [vifi] = 0;
+ /*XXX isn't uv_nbrmap going to be empty?*/
+ NBRM_CLRMASK(r->rt_subordinates, v->uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ }
+ }
+}
+
+
+/*
+ * A vif has gone down -- expire all routes that have that vif as parent,
+ * and update the children bitmaps in all other route entries to take into
+ * account the failed vif.
+ */
+void
+delete_vif_from_routes(vifi)
+ register vifi_t vifi;
+{
+ register struct rtentry *r;
+
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE) {
+ if (vifi == r->rt_parent) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ else if (VIFM_ISSET(vifi, r->rt_children)) {
+ VIFM_CLR(vifi, r->rt_children);
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ }
+ else {
+ r->rt_dominants[vifi] = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ * A new neighbor has come up. If we're flooding on the neighbor's
+ * vif, mark that neighbor as subordinate for all routes whose parent
+ * is not this vif.
+ */
+void
+add_neighbor_to_routes(vifi, index)
+ register vifi_t vifi;
+ register int index;
+{
+ register struct rtentry *r;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ if (v->uv_flags & VIFF_NOFLOOD)
+ return;
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE && r->rt_parent != vifi &&
+ !AVOID_TRANSIT(vifi, r)) {
+ NBRM_SET(index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ }
+ }
+}
+
+
+/*
+ * A neighbor has failed or become unreachable. If that neighbor was
+ * considered a dominant or subordinate router in any route entries,
+ * take appropriate action. Expire all routes this neighbor advertised
+ * to us.
+ */
+void
+delete_neighbor_from_routes(addr, vifi, index)
+ register u_int32 addr;
+ register vifi_t vifi;
+ int index;
+{
+ register struct rtentry *r;
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ if (r->rt_metric != UNREACHABLE) {
+ if (r->rt_parent == vifi && r->rt_gateway == addr) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ } else if (r->rt_dominants[vifi] == addr) {
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants[vifi] = 0;
+ if ((uvifs[vifi].uv_flags & VIFF_NOFLOOD) ||
+ AVOID_TRANSIT(vifi, r))
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ } else if (NBRM_ISSET(index, r->rt_subordinates)) {
+ NBRM_CLR(index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ }
+ }
+ }
+}
+
+
+/*
+ * Prepare for a sequence of ordered route updates by initializing a pointer
+ * to the start of the routing table. The pointer is used to remember our
+ * position in the routing table in order to avoid searching from the
+ * beginning for each update; this relies on having the route reports in
+ * a single message be in the same order as the route entries in the routing
+ * table.
+ */
+void
+start_route_updates()
+{
+ rtp = RT_ADDR;
+}
+
+
+/*
+ * Starting at the route entry following the one to which 'rtp' points,
+ * look for a route entry matching the specified origin and mask. If a
+ * match is found, return TRUE and leave 'rtp' pointing at the found entry.
+ * If no match is found, return FALSE and leave 'rtp' pointing to the route
+ * entry preceding the point at which the new origin should be inserted.
+ * This code is optimized for the normal case in which the first entry to
+ * be examined is the matching entry.
+ */
+static int
+find_route(origin, mask)
+ register u_int32 origin, mask;
+{
+ register struct rtentry *r;
+
+ r = rtp->rt_next;
+ while (r != NULL) {
+ if (origin == r->rt_origin && mask == r->rt_originmask) {
+ rtp = r;
+ return (TRUE);
+ }
+ if (ntohl(mask) < ntohl(r->rt_originmask) ||
+ (mask == r->rt_originmask &&
+ ntohl(origin) < ntohl(r->rt_origin))) {
+ rtp = r;
+ r = r->rt_next;
+ }
+ else break;
+ }
+ return (FALSE);
+}
+
+/*
+ * Create a new routing table entry for the specified origin and link it into
+ * the routing table. The shared variable 'rtp' is assumed to point to the
+ * routing entry after which the new one should be inserted. It is left
+ * pointing to the new entry.
+ *
+ * Only the origin, originmask, originwidth and flags fields are initialized
+ * in the new route entry; the caller is responsible for filling in the the
+ * rest.
+ */
+static void
+create_route(origin, mask)
+ u_int32 origin, mask;
+{
+ register struct rtentry *r;
+
+ if ((r = (struct rtentry *) malloc(sizeof(struct rtentry) +
+ (numvifs * sizeof(u_int32)))) == NULL) {
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+ }
+ r->rt_origin = origin;
+ r->rt_originmask = mask;
+ if (((char *)&mask)[3] != 0) r->rt_originwidth = 4;
+ else if (((char *)&mask)[2] != 0) r->rt_originwidth = 3;
+ else if (((char *)&mask)[1] != 0) r->rt_originwidth = 2;
+ else r->rt_originwidth = 1;
+ r->rt_flags = 0;
+ r->rt_dominants = (u_int32 *)(r + 1);
+ bzero(r->rt_dominants, numvifs * sizeof(u_int32));
+ r->rt_groups = NULL;
+ VIFM_CLRALL(r->rt_children);
+ NBRM_CLRALL(r->rt_subordinates);
+ NBRM_CLRALL(r->rt_subordadv);
+
+ r->rt_next = rtp->rt_next;
+ rtp->rt_next = r;
+ r->rt_prev = rtp;
+ if (r->rt_next != NULL)
+ (r->rt_next)->rt_prev = r;
+ else
+ rt_end = r;
+ rtp = r;
+ ++nroutes;
+}
+
+
+/*
+ * Discard the routing table entry following the one to which 'prev_r' points.
+ */
+static void
+discard_route(prev_r)
+ register struct rtentry *prev_r;
+{
+ register struct rtentry *r;
+
+ r = prev_r->rt_next;
+ uvifs[r->rt_parent].uv_nroutes--;
+ /*???nbr???.al_nroutes--;*/
+ prev_r->rt_next = r->rt_next;
+ if (prev_r->rt_next != NULL)
+ (prev_r->rt_next)->rt_prev = prev_r;
+ else
+ rt_end = prev_r;
+ free((char *)r);
+ --nroutes;
+}
+
+
+/*
+ * Process a route report for a single origin, creating or updating the
+ * corresponding routing table entry if necessary. 'src' is either the
+ * address of a neighboring router from which the report arrived, or zero
+ * to indicate a change of status of one of our own interfaces.
+ */
+void
+update_route(origin, mask, metric, src, vifi, n)
+ u_int32 origin, mask;
+ u_int metric;
+ u_int32 src;
+ vifi_t vifi;
+ struct listaddr *n;
+{
+ register struct rtentry *r;
+ u_int adj_metric;
+
+ /*
+ * Compute an adjusted metric, taking into account the cost of the
+ * subnet or tunnel over which the report arrived, and normalizing
+ * all unreachable/poisoned metrics into a single value.
+ */
+ if (src != 0 && (metric < 1 || metric >= 2*UNREACHABLE)) {
+ log(LOG_WARNING, 0,
+ "%s reports out-of-range metric %u for origin %s",
+ inet_fmt(src, s1), metric, inet_fmts(origin, mask, s2));
+ return;
+ }
+ adj_metric = metric + uvifs[vifi].uv_metric;
+ if (adj_metric > UNREACHABLE) adj_metric = UNREACHABLE;
+
+ /*
+ * Look up the reported origin in the routing table.
+ */
+ if (!find_route(origin, mask)) {
+ /*
+ * Not found.
+ * Don't create a new entry if the report says it's unreachable,
+ * or if the reported origin and mask are invalid.
+ */
+ if (adj_metric == UNREACHABLE) {
+ return;
+ }
+ if (src != 0 && !inet_valid_subnet(origin, mask)) {
+ log(LOG_WARNING, 0,
+ "%s reports an invalid origin (%s) and/or mask (%08x)",
+ inet_fmt(src, s1), inet_fmt(origin, s2), ntohl(mask));
+ return;
+ }
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s advertises new route %s",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2));
+
+ /*
+ * OK, create the new routing entry. 'rtp' will be left pointing
+ * to the new entry.
+ */
+ create_route(origin, mask);
+ uvifs[vifi].uv_nroutes++;
+ /*n->al_nroutes++;*/
+
+ rtp->rt_metric = UNREACHABLE; /* temporary; updated below */
+ }
+
+ /*
+ * We now have a routing entry for the reported origin. Update it?
+ */
+ r = rtp;
+ if (r->rt_metric == UNREACHABLE) {
+ /*
+ * The routing entry is for a formerly-unreachable or new origin.
+ * If the report claims reachability, update the entry to use
+ * the reported route.
+ */
+ if (adj_metric == UNREACHABLE)
+ return;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s advertises %s with adj_metric %d (ours was %d)",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2),
+ adj_metric, r->rt_metric);
+
+ /*
+ * Now "steal away" any sources that belong under this route
+ * by deleting any cache entries they might have created
+ * and allowing the kernel to re-request them.
+ *
+ * If we haven't performed final initialization yet and are
+ * just collecting the routing table, we can't have any
+ * sources so we don't perform this step.
+ */
+ if (did_final_init)
+ steal_sources(rtp);
+
+ r->rt_parent = vifi;
+ r->rt_gateway = src;
+ init_children_and_leaves(r, vifi, 1);
+
+ r->rt_timer = 0;
+ r->rt_metric = adj_metric;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ update_table_entry(r, r->rt_gateway);
+ }
+ else if (src == r->rt_gateway) {
+ /*
+ * The report has come either from the interface directly-connected
+ * to the origin subnet (src and r->rt_gateway both equal zero) or
+ * from the gateway we have chosen as the best first-hop gateway back
+ * towards the origin (src and r->rt_gateway not equal zero). Reset
+ * the route timer and, if the reported metric has changed, update
+ * our entry accordingly.
+ */
+ r->rt_timer = 0;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (current parent) advertises %s with adj_metric %d (ours was %d)",
+ inet_fmt(src, s1), inet_fmts(origin, mask, s2),
+ adj_metric, r->rt_metric);
+
+ if (adj_metric == r->rt_metric)
+ return;
+
+ if (adj_metric == UNREACHABLE) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ }
+ r->rt_metric = adj_metric;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ else if (src == 0 ||
+ (r->rt_gateway != 0 &&
+ (adj_metric < r->rt_metric ||
+ (adj_metric == r->rt_metric &&
+ (ntohl(src) < ntohl(r->rt_gateway) ||
+ r->rt_timer >= ROUTE_SWITCH_TIME))))) {
+ /*
+ * The report is for an origin we consider reachable; the report
+ * comes either from one of our own interfaces or from a gateway
+ * other than the one we have chosen as the best first-hop gateway
+ * back towards the origin. If the source of the update is one of
+ * our own interfaces, or if the origin is not a directly-connected
+ * subnet and the reported metric for that origin is better than
+ * what our routing entry says, update the entry to use the new
+ * gateway and metric. We also switch gateways if the reported
+ * metric is the same as the one in the route entry and the gateway
+ * associated with the route entry has not been heard from recently,
+ * or if the metric is the same but the reporting gateway has a lower
+ * IP address than the gateway associated with the route entry.
+ * Did you get all that?
+ */
+ u_int32 old_gateway;
+ vifi_t old_parent;
+ old_gateway = r->rt_gateway;
+ old_parent = r->rt_parent;
+ r->rt_gateway = src;
+ r->rt_parent = vifi;
+
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (new parent) on vif %d advertises %s with adj_metric %d (old parent was %s on vif %d, metric %d)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ adj_metric, inet_fmt(old_gateway, s3), old_parent,
+ r->rt_metric);
+
+ if (old_parent != vifi) {
+ init_children_and_leaves(r, vifi, 0);
+ uvifs[old_parent].uv_nroutes--;
+ uvifs[vifi].uv_nroutes++;
+ }
+ if (old_gateway != src) {
+ update_table_entry(r, old_gateway);
+ /*???old_gateway???->al_nroutes--;*/
+ /*n->al_nroutes++;*/
+ }
+ r->rt_timer = 0;
+ r->rt_metric = adj_metric;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ else if (vifi != r->rt_parent) {
+ /*
+ * The report came from a vif other than the route's parent vif.
+ * Update the children info, if necessary.
+ */
+ if (AVOID_TRANSIT(vifi, r)) {
+ /*
+ * The route's parent is a vif from which we're not supposed
+ * to transit onto this vif. Simply ignore the update.
+ */
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored due to NOTRANSIT)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ } else if (VIFM_ISSET(vifi, r->rt_children)) {
+ /*
+ * Vif is a child vif for this route.
+ */
+ if (metric < r->rt_metric ||
+ (metric == r->rt_metric &&
+ ntohl(src) < ntohl(uvifs[vifi].uv_lcl_addr))) {
+ /*
+ * Neighbor has lower metric to origin (or has same metric
+ * and lower IP address) -- it becomes the dominant router,
+ * and vif is no longer a child for me.
+ */
+ VIFM_CLR(vifi, r->rt_children);
+ r->rt_dominants [vifi] = src;
+ /* XXX
+ * We don't necessarily want to forget about subordinateness
+ * so that we can become the dominant quickly if the current
+ * dominant fails.
+ */
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ update_table_entry(r, r->rt_gateway);
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d becomes dominant for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ }
+ else if (metric > UNREACHABLE) { /* "poisoned reverse" */
+ /*
+ * Neighbor considers this vif to be on path to route's
+ * origin; record this neighbor as subordinate
+ */
+ if (!NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d becomes subordinate for %s with poison-reverse metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric - UNREACHABLE);
+ NBRM_SET(n->al_index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ } else {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d confirms subordinateness for %s with poison-reverse metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric - UNREACHABLE);
+ }
+ NBRM_SET(n->al_index, r->rt_subordadv);
+ }
+ else if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ /*
+ * Current subordinate no longer considers this vif to be on
+ * path to route's origin; it is no longer a subordinate
+ * router.
+ */
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d is no longer a subordinate for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ NBRM_CLR(n->al_index, r->rt_subordinates);
+ update_table_entry(r, r->rt_gateway);
+ }
+
+ }
+ else if (src == r->rt_dominants[vifi] &&
+ (metric > r->rt_metric ||
+ (metric == r->rt_metric &&
+ ntohl(src) > ntohl(uvifs[vifi].uv_lcl_addr)))) {
+ /*
+ * Current dominant no longer has a lower metric to origin
+ * (or same metric and lower IP address); we adopt the vif
+ * as our own child.
+ */
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s (current dominant) on vif %d is no longer dominant for %s with metric %d",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants[vifi] = 0;
+ if (uvifs[vifi].uv_flags & VIFF_NOFLOOD)
+ NBRM_CLRMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ else
+ NBRM_SETMASK(r->rt_subordinates, uvifs[vifi].uv_nbrmap);
+ if (metric > UNREACHABLE) {
+ NBRM_SET(n->al_index, r->rt_subordinates);
+ NBRM_SET(n->al_index, r->rt_subordadv);
+ }
+ update_table_entry(r, r->rt_gateway);
+ } else {
+ IF_DEBUG(DEBUG_RTDETAIL)
+ log(LOG_DEBUG, 0, "%s on vif %d advertises %s with metric %d (ignored)",
+ inet_fmt(src, s1), vifi, inet_fmts(origin, mask, s2),
+ metric);
+ }
+ }
+}
+
+
+/*
+ * On every timer interrupt, advance the timer in each routing entry.
+ */
+void
+age_routes()
+{
+ register struct rtentry *r;
+ register struct rtentry *prev_r;
+ extern u_long virtual_time; /* from main.c */
+
+ for (prev_r = RT_ADDR, r = routing_table;
+ r != NULL;
+ prev_r = r, r = r->rt_next) {
+
+ if ((r->rt_timer += TIMER_INTERVAL) >= ROUTE_DISCARD_TIME) {
+ /*
+ * Time to garbage-collect the route entry.
+ */
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ discard_route(prev_r);
+ r = prev_r;
+ }
+ else if (r->rt_timer >= ROUTE_EXPIRE_TIME &&
+ r->rt_metric != UNREACHABLE) {
+ /*
+ * Time to expire the route entry. If the gateway is zero,
+ * i.e., it is a route to a directly-connected subnet, just
+ * set the timer back to zero; such routes expire only when
+ * the interface to the subnet goes down.
+ */
+ if (r->rt_gateway == 0) {
+ r->rt_timer = 0;
+ }
+ else {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+ }
+ else if (virtual_time % (ROUTE_REPORT_INTERVAL * 2) == 0) {
+ /*
+ * Time out subordinateness that hasn't been reported in
+ * the last 2 intervals.
+ */
+ if (!NBRM_SAME(r->rt_subordinates, r->rt_subordadv)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "rt %s sub 0x%08x%08x subadv 0x%08x%08x metric %d",
+ RT_FMT(r, s1),
+ r->rt_subordinates.hi, r->rt_subordinates.lo,
+ r->rt_subordadv.hi, r->rt_subordadv.lo, r->rt_metric);
+ NBRM_MASK(r->rt_subordinates, r->rt_subordadv);
+ update_table_entry(r, r->rt_gateway);
+ }
+ NBRM_CLRALL(r->rt_subordadv);
+ }
+ }
+}
+
+
+/*
+ * Mark all routes as unreachable. This function is called only from
+ * hup() in preparation for informing all neighbors that we are going
+ * off the air. For consistency, we ought also to delete all reachable
+ * route entries from the kernel, but since we are about to exit we rely
+ * on the kernel to do its own cleanup -- no point in making all those
+ * expensive kernel calls now.
+ */
+void
+expire_all_routes()
+{
+ register struct rtentry *r;
+
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ r->rt_metric = UNREACHABLE;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ }
+}
+
+
+/*
+ * Delete all the routes in the routing table.
+ */
+void
+free_all_routes()
+{
+ register struct rtentry *r;
+
+ r = RT_ADDR;
+
+ while (r->rt_next)
+ discard_route(r);
+}
+
+
+/*
+ * Process an incoming neighbor probe message.
+ */
+void
+accept_probe(src, dst, p, datalen, level)
+ u_int32 src;
+ u_int32 dst;
+ char *p;
+ int datalen;
+ u_int32 level;
+{
+ vifi_t vifi;
+ static struct listaddr *unknowns = NULL;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ struct listaddr *a, **prev;
+ struct listaddr *match = NULL;
+ time_t now = time(0);
+
+ for (prev = &unknowns, a = *prev; a; a = *prev) {
+ if (a->al_addr == src)
+ match = a;
+ if (a->al_ctime + 2 * a->al_timer < now) {
+ /* We haven't heard from it in a long time */
+ *prev = a->al_next;
+ free(a);
+ } else {
+ prev = &a->al_next;
+ }
+ }
+ if (match == NULL) {
+ match = *prev = (struct listaddr *)malloc(sizeof(struct listaddr));
+ match->al_next = NULL;
+ match->al_addr = src;
+ match->al_timer = OLD_NEIGHBOR_EXPIRE_TIME;
+ match->al_ctime = now - match->al_timer;
+ }
+
+ if (match->al_ctime + match->al_timer <= now) {
+ log(LOG_WARNING, 0,
+ "ignoring probe from non-neighbor %s, check for misconfigured tunnel or routing on %s",
+ inet_fmt(src, s1), s1);
+ match->al_timer *= 2;
+ } else
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0,
+ "ignoring probe from non-neighbor %s (%d seconds until next warning)", inet_fmt(src, s1), match->al_ctime + match->al_timer - now);
+ return;
+ }
+
+ update_neighbor(vifi, src, DVMRP_PROBE, p, datalen, level);
+}
+
+struct newrt {
+ u_int32 mask;
+ u_int32 origin;
+ int metric;
+ int pad;
+};
+
+static int
+compare_rts(rt1, rt2)
+ const void *rt1;
+ const void *rt2;
+{
+ register struct newrt *r1 = (struct newrt *)rt1;
+ register struct newrt *r2 = (struct newrt *)rt2;
+ register u_int32 m1 = ntohl(r1->mask);
+ register u_int32 m2 = ntohl(r2->mask);
+ register u_int32 o1, o2;
+
+ if (m1 > m2)
+ return (-1);
+ if (m1 < m2)
+ return (1);
+
+ /* masks are equal */
+ o1 = ntohl(r1->origin);
+ o2 = ntohl(r2->origin);
+ if (o1 > o2)
+ return (-1);
+ if (o1 < o2)
+ return (1);
+ return (0);
+}
+
+void
+blaster_alloc(vifi)
+ vifi_t vifi;
+{
+ register struct uvif *v;
+
+ v = &uvifs[vifi];
+ if (v->uv_blasterbuf)
+ free(v->uv_blasterbuf);
+
+ v->uv_blasterlen = 64*1024;
+ v->uv_blasterbuf = malloc(v->uv_blasterlen);
+ v->uv_blastercur = v->uv_blasterend = v->uv_blasterbuf;
+ if (v->uv_blastertimer)
+ timer_clearTimer(v->uv_blastertimer);
+ v->uv_blastertimer = 0;
+}
+
+struct blaster_hdr {
+ u_int32 bh_src;
+ u_int32 bh_dst;
+ u_int32 bh_level;
+ int bh_datalen;
+};
+
+/*
+ * Queue a route report from a route-blaster.
+ * If the timer isn't running to process these reports,
+ * start it.
+ */
+static void
+queue_blaster_report(vifi, src, dst, p, datalen, level)
+ vifi_t vifi;
+ u_int32 src, dst, level;
+ register char *p;
+ register int datalen;
+{
+ register struct blaster_hdr *bh;
+ register struct uvif *v;
+ int bblen = sizeof(*bh) + ((datalen + 3) & ~3);
+
+ v = &uvifs[vifi];
+ if (v->uv_blasterend - v->uv_blasterbuf +
+ bblen > v->uv_blasterlen) {
+ int end = v->uv_blasterend - v->uv_blasterbuf;
+ int cur = v->uv_blastercur - v->uv_blasterbuf;
+
+ v->uv_blasterlen *= 2;
+ IF_DEBUG(DEBUG_IF)
+ log(LOG_DEBUG, 0, "increasing blasterbuf to %d bytes",
+ v->uv_blasterlen);
+ v->uv_blasterbuf = realloc(v->uv_blasterbuf,
+ v->uv_blasterlen);
+ if (v->uv_blasterbuf == NULL) {
+ log(LOG_WARNING, ENOMEM, "turning off blaster on vif %d", vifi);
+ v->uv_blasterlen = 0;
+ v->uv_blasterend = v->uv_blastercur = NULL;
+ v->uv_flags &= ~VIFF_BLASTER;
+ return;
+ }
+ v->uv_blasterend = v->uv_blasterbuf + end;
+ v->uv_blastercur = v->uv_blasterbuf + cur;
+ }
+ bh = (struct blaster_hdr *)v->uv_blasterend;
+ bh->bh_src = src;
+ bh->bh_dst = dst;
+ bh->bh_level = level;
+ bh->bh_datalen = datalen;
+ bcopy(p, (char *)(bh + 1), datalen);
+ v->uv_blasterend += bblen;
+
+ if (v->uv_blastertimer == 0) {
+ int *i = (int *)malloc(sizeof(int *));
+
+ if (i == NULL)
+ log(LOG_ERR, 0, "out of memory");
+
+ *i = vifi;
+
+ v->uv_blastertimer = timer_setTimer(5,
+ process_blaster_report, i);
+ }
+}
+
+/*
+ * Periodic process; process up to 5 of the routes in the route-blaster
+ * queue. If there are more routes remaining, reschedule myself to run
+ * in 1 second.
+ */
+static void
+process_blaster_report(vifip)
+ void *vifip;
+{
+ vifi_t vifi = *(int *)vifip;
+ register struct uvif *v;
+ register struct blaster_hdr *bh;
+ int i;
+
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "processing vif %d blasted routes", vifi);
+ v = &uvifs[vifi];
+ for (i = 0; i < 5; i++) {
+ if (v->uv_blastercur >= v->uv_blasterend)
+ break;
+ bh = (struct blaster_hdr *)v->uv_blastercur;
+ v->uv_blastercur += sizeof(*bh) + ((bh->bh_datalen + 3) & ~3);
+ accept_report(bh->bh_src, bh->bh_dst, (char *)(bh + 1),
+ -bh->bh_datalen, bh->bh_level);
+ }
+
+ if (v->uv_blastercur >= v->uv_blasterend) {
+ v->uv_blastercur = v->uv_blasterbuf;
+ v->uv_blasterend = v->uv_blasterbuf;
+ v->uv_blastertimer = 0;
+ free(vifip);
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "finish processing vif %d blaster", vifi);
+ } else {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "more blasted routes to come on vif %d", vifi);
+ v->uv_blastertimer = timer_setTimer(1,
+ process_blaster_report, vifip);
+ }
+}
+
+/*
+ * Process an incoming route report message.
+ * If the report arrived on a vif marked as a "blaster", then just
+ * queue it and return; queue_blaster_report() will schedule it for
+ * processing later. If datalen is negative, then this is actually
+ * a queued report so actually process it instead of queueing it.
+ */
+void
+accept_report(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ register char *p;
+ register int datalen;
+{
+ vifi_t vifi;
+ register int width, i, nrt = 0;
+ int metric;
+ u_int32 mask;
+ u_int32 origin;
+ struct newrt rt[4096];
+ struct listaddr *nbr;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring route report from non-neighbor %s", inet_fmt(src, s1));
+ return;
+ }
+
+ if (uvifs[vifi].uv_flags & VIFF_BLASTER)
+ if (datalen > 0) {
+ queue_blaster_report(vifi, src, dst, p, datalen, level);
+ return;
+ } else {
+ datalen = -datalen;
+ }
+
+ if (!(nbr = update_neighbor(vifi, src, DVMRP_REPORT, NULL, 0, level)))
+ return;
+
+ if (datalen > 2*4096) {
+ log(LOG_INFO, 0,
+ "ignoring oversize (%d bytes) route report from %s",
+ datalen, inet_fmt(src, s1));
+ return;
+ }
+
+ while (datalen > 0) { /* Loop through per-mask lists. */
+
+ if (datalen < 3) {
+ log(LOG_WARNING, 0,
+ "received truncated route report from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+ ((u_char *)&mask)[0] = 0xff; width = 1;
+ if ((((u_char *)&mask)[1] = *p++) != 0) width = 2;
+ if ((((u_char *)&mask)[2] = *p++) != 0) width = 3;
+ if ((((u_char *)&mask)[3] = *p++) != 0) width = 4;
+ if (!inet_valid_mask(ntohl(mask))) {
+ log(LOG_WARNING, 0,
+ "%s reports bogus netmask 0x%08x (%s)",
+ inet_fmt(src, s1), ntohl(mask), inet_fmt(mask, s2));
+ return;
+ }
+ datalen -= 3;
+
+ do { /* Loop through (origin, metric) pairs */
+ if (datalen < width + 1) {
+ log(LOG_WARNING, 0,
+ "received truncated route report from %s",
+ inet_fmt(src, s1));
+ return;
+ }
+ origin = 0;
+ for (i = 0; i < width; ++i)
+ ((char *)&origin)[i] = *p++;
+ metric = *p++;
+ datalen -= width + 1;
+ rt[nrt].mask = mask;
+ rt[nrt].origin = origin;
+ rt[nrt].metric = (metric & 0x7f);
+ ++nrt;
+ } while (!(metric & 0x80));
+ }
+
+ qsort((char*)rt, nrt, sizeof(rt[0]), compare_rts);
+ start_route_updates();
+ /*
+ * If the last entry is default, change mask from 0xff000000 to 0
+ */
+ if (rt[nrt-1].origin == 0)
+ rt[nrt-1].mask = 0;
+
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "Updating %d routes from %s to %s", nrt,
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+ for (i = 0; i < nrt; ++i) {
+ if (i != 0 && rt[i].origin == rt[i-1].origin &&
+ rt[i].mask == rt[i-1].mask) {
+ log(LOG_WARNING, 0, "%s reports duplicate route for %s",
+ inet_fmt(src, s1), inet_fmts(rt[i].origin, rt[i].mask, s2));
+ continue;
+ }
+ /* Only filter non-poisoned updates. */
+ if (uvifs[vifi].uv_filter && rt[i].metric < UNREACHABLE) {
+ struct vf_element *vfe;
+ int match = 0;
+
+ for (vfe = uvifs[vifi].uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
+ if (vfe->vfe_flags & VFEF_EXACT) {
+ if ((vfe->vfe_addr == rt[i].origin) &&
+ (vfe->vfe_mask == rt[i].mask)) {
+ match = 1;
+ break;
+ }
+ } else {
+ if ((rt[i].origin & vfe->vfe_mask) == vfe->vfe_addr) {
+ match = 1;
+ break;
+ }
+ }
+ }
+ if ((uvifs[vifi].uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
+ (uvifs[vifi].uv_filter->vf_type == VFT_DENY && match == 1)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s skipped on vif %d because it %s %s",
+ inet_fmts(rt[i].origin, rt[i].mask, s1),
+ vifi,
+ match ? "matches" : "doesn't match",
+ match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
+ "the filter");
+#if 0
+ rt[i].metric += vfe->vfe_addmetric;
+ if (rt[i].metric > UNREACHABLE)
+#endif
+ rt[i].metric = UNREACHABLE;
+ }
+ }
+ update_route(rt[i].origin, rt[i].mask, rt[i].metric,
+ src, vifi, nbr);
+ }
+
+ if (routes_changed && !delay_change_reports)
+ report_to_all_neighbors(CHANGED_ROUTES);
+}
+
+
+/*
+ * Send a route report message to destination 'dst', via virtual interface
+ * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
+ */
+void
+report(which_routes, vifi, dst)
+ int which_routes;
+ vifi_t vifi;
+ u_int32 dst;
+{
+ register struct rtentry *r;
+ register int i;
+
+ r = rt_end;
+ while (r != RT_ADDR) {
+ i = report_chunk(which_routes, r, vifi, dst);
+ while (i-- > 0)
+ r = r->rt_prev;
+ }
+}
+
+
+/*
+ * Send a route report message to all neighboring routers.
+ * 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
+ */
+void
+report_to_all_neighbors(which_routes)
+ int which_routes;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct rtentry *r;
+ int routes_changed_before;
+
+ /*
+ * Remember the state of the global routes_changed flag before
+ * generating the reports, and clear the flag.
+ */
+ routes_changed_before = routes_changed;
+ routes_changed = FALSE;
+
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
+ report(which_routes, vifi, v->uv_dst_addr);
+ }
+ }
+
+ /*
+ * If there were changed routes before we sent the reports AND
+ * if no new changes occurred while sending the reports, clear
+ * the change flags in the individual route entries. If changes
+ * did occur while sending the reports, new reports will be
+ * generated at the next timer interrupt.
+ */
+ if (routes_changed_before && !routes_changed) {
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+ r->rt_flags &= ~RTF_CHANGED;
+ }
+ }
+
+ /*
+ * Set a flag to inhibit further reports of changed routes until the
+ * next timer interrupt. This is to alleviate update storms.
+ */
+ delay_change_reports = TRUE;
+}
+
+/*
+ * Send a route report message to destination 'dst', via virtual interface
+ * 'vifi'. 'which_routes' specifies ALL_ROUTES or CHANGED_ROUTES.
+ */
+static int
+report_chunk(which_routes, start_rt, vifi, dst)
+ int which_routes;
+ register struct rtentry *start_rt;
+ vifi_t vifi;
+ u_int32 dst;
+{
+ register struct rtentry *r;
+ register char *p;
+ register int i;
+ register int nrt = 0;
+ struct uvif *v = &uvifs[vifi];
+ int datalen = 0;
+ int width = 0;
+ u_int32 mask = 0;
+ u_int32 src;
+ int admetric = v->uv_admetric;
+ int metric;
+
+ src = v->uv_lcl_addr;
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ for (r = start_rt; r != RT_ADDR; r = r->rt_prev) {
+ if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED)) {
+ nrt++;
+ continue;
+ }
+
+ /*
+ * Do not poison-reverse a route for a directly-connected
+ * subnetwork on that subnetwork. This can cause loops when
+ * some router on the subnetwork is misconfigured.
+ */
+ if (r->rt_gateway == 0 && r->rt_parent == vifi) {
+ nrt++;
+ continue;
+ }
+
+ if (v->uv_filter && v->uv_filter->vf_flags & VFF_BIDIR) {
+ struct vf_element *vfe;
+ int match = 0;
+
+ for (vfe = v->uv_filter->vf_filter; vfe; vfe = vfe->vfe_next) {
+ if (vfe->vfe_flags & VFEF_EXACT) {
+ if ((vfe->vfe_addr == r->rt_origin) &&
+ (vfe->vfe_mask == r->rt_originmask)) {
+ match = 1;
+ break;
+ }
+ } else {
+ if ((r->rt_origin & vfe->vfe_mask) == vfe->vfe_addr) {
+ match = 1;
+ break;
+ }
+ }
+ }
+ if ((v->uv_filter->vf_type == VFT_ACCEPT && match == 0) ||
+ (v->uv_filter->vf_type == VFT_DENY && match == 1)) {
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_DEBUG, 0, "%s not reported on vif %d because it %s %s",
+ RT_FMT(r, s1), vifi,
+ match ? "matches" : "doesn't match",
+ match ? inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s2) :
+ "the filter");
+ nrt++;
+ continue;
+ }
+ }
+
+ /*
+ * If there is no room for this route in the current message,
+ * send it & return how many routes we sent.
+ */
+ if (datalen + ((r->rt_originmask == mask) ?
+ (width + 1) :
+ (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
+ *(p-1) |= 0x80;
+ send_on_vif(v, 0, DVMRP_REPORT, datalen);
+ return (nrt);
+ }
+
+ if (r->rt_originmask != mask || datalen == 0) {
+ mask = r->rt_originmask;
+ width = r->rt_originwidth;
+ if (datalen != 0) *(p-1) |= 0x80;
+ *p++ = ((char *)&mask)[1];
+ *p++ = ((char *)&mask)[2];
+ *p++ = ((char *)&mask)[3];
+ datalen += 3;
+ }
+ for (i = 0; i < width; ++i)
+ *p++ = ((char *)&(r->rt_origin))[i];
+
+ metric = r->rt_metric + admetric;
+ if (metric > UNREACHABLE)
+ metric = UNREACHABLE;
+ if (r->rt_parent != vifi && AVOID_TRANSIT(vifi, r))
+ metric = UNREACHABLE;
+ *p++ = (r->rt_parent == vifi && metric != UNREACHABLE) ?
+ (char)(metric + UNREACHABLE) : /* "poisoned reverse" */
+ (char)(metric);
+ ++nrt;
+ datalen += width + 1;
+ }
+ if (datalen != 0) {
+ *(p-1) |= 0x80;
+ send_on_vif(v, 0, DVMRP_REPORT, datalen);
+ }
+ return (nrt);
+}
+
+/*
+ * send the next chunk of our routing table to all neighbors.
+ * return the length of the smallest chunk we sent out.
+ */
+int
+report_next_chunk()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct rtentry *sr;
+ register int i, n = 0, min = 20000;
+ static int start_rt;
+
+ if (nroutes <= 0)
+ return (0);
+
+ /*
+ * find this round's starting route.
+ */
+ for (sr = rt_end, i = start_rt; --i >= 0; ) {
+ sr = sr->rt_prev;
+ if (sr == RT_ADDR)
+ sr = rt_end;
+ }
+
+ /*
+ * send one chunk of routes starting at this round's start to
+ * all our neighbors.
+ */
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!NBRM_ISEMPTY(v->uv_nbrmap)) {
+ n = report_chunk(ALL_ROUTES, sr, vifi, v->uv_dst_addr);
+ if (n < min)
+ min = n;
+ }
+ }
+ if (min == 20000)
+ min = 0; /* Neighborless router didn't send any routes */
+
+ n = min;
+ IF_DEBUG(DEBUG_ROUTE)
+ log(LOG_INFO, 0, "update %d starting at %d of %d",
+ n, (nroutes - start_rt), nroutes);
+
+ start_rt = (start_rt + n) % nroutes;
+ return (n);
+}
+
+
+/*
+ * Print the contents of the routing table on file 'fp'.
+ */
+void
+dump_routes(fp)
+ FILE *fp;
+{
+ register struct rtentry *r;
+ register vifi_t i;
+
+
+ fprintf(fp,
+ "Multicast Routing Table (%u %s)\n%s\n",
+ nroutes, (nroutes == 1) ? "entry" : "entries",
+ " Origin-Subnet From-Gateway Metric Tmr Fl In-Vif Out-Vifs");
+
+ for (r = routing_table; r != NULL; r = r->rt_next) {
+
+ fprintf(fp, " %-18s %-15s ",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ (r->rt_gateway == 0) ? "" : inet_fmt(r->rt_gateway, s2));
+
+ fprintf(fp, (r->rt_metric == UNREACHABLE) ? " NR " : "%4u ",
+ r->rt_metric);
+
+ fprintf(fp, " %3u %c%c %3u ", r->rt_timer,
+ (r->rt_flags & RTF_CHANGED) ? 'C' : '.',
+ (r->rt_flags & RTF_HOLDDOWN) ? 'H' : '.',
+ r->rt_parent);
+
+ for (i = 0; i < numvifs; ++i) {
+ struct listaddr *n;
+ char l = '[';
+
+ if (VIFM_ISSET(i, r->rt_children)) {
+ if ((uvifs[i].uv_flags & VIFF_TUNNEL) &&
+ !NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ /* Don't print out parenthood of a leaf tunnel. */
+ continue;
+ fprintf(fp, " %u", i);
+ if (!NBRM_ISSETMASK(uvifs[i].uv_nbrmap, r->rt_subordinates))
+ fprintf(fp, "*");
+ for (n = uvifs[i].uv_neighbors; n; n = n->al_next) {
+ if (NBRM_ISSET(n->al_index, r->rt_subordinates)) {
+ fprintf(fp, "%c%d", l, n->al_index);
+ l = ',';
+ }
+ }
+ if (l == ',')
+ fprintf(fp, "]");
+ }
+ }
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "\n");
+}
+
+struct rtentry *
+determine_route(src)
+ u_int32 src;
+{
+ struct rtentry *rt;
+
+ for (rt = routing_table; rt != NULL; rt = rt->rt_next) {
+ if (rt->rt_origin == (src & rt->rt_originmask) &&
+ rt->rt_metric != UNREACHABLE)
+ break;
+ }
+ return rt;
+}
diff --git a/usr.sbin/mrouted/route.h b/usr.sbin/mrouted/route.h
new file mode 100644
index 0000000..300202b
--- /dev/null
+++ b/usr.sbin/mrouted/route.h
@@ -0,0 +1,53 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * route.h,v 3.8.4.6 1997/07/01 23:02:35 fenner Exp
+ */
+
+/*
+ * Routing Table Entry, one per subnet from which a multicast could originate.
+ * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
+ *
+ * The Routing Table is stored as a doubly-linked list of these structures,
+ * ordered by decreasing value of rt_originmask and, secondarily, by
+ * decreasing value of rt_origin within each rt_originmask value.
+ * This data structure is efficient for generating route reports, whether
+ * full or partial, for processing received full reports, for clearing the
+ * CHANGED flags, and for periodically advancing the timers in all routes.
+ * It is not so efficient for updating a small number of routes in response
+ * to a partial report. In a stable topology, the latter are rare; if they
+ * turn out to be costing a lot, we can add an auxiliary hash table for
+ * faster access to arbitrary route entries.
+ */
+struct rtentry {
+ struct rtentry *rt_next; /* link to next entry MUST BE FIRST */
+ u_int32 rt_origin; /* subnet origin of multicasts */
+ u_int32 rt_originmask; /* subnet mask for origin */
+ short rt_originwidth; /* # bytes of origin subnet number */
+ u_char rt_metric; /* cost of route back to origin */
+ u_char rt_flags; /* RTF_ flags defined below */
+ u_int32 rt_gateway; /* first-hop gateway back to origin */
+ vifi_t rt_parent; /* incoming vif (ie towards origin) */
+ vifbitmap_t rt_children; /* outgoing children vifs */
+ u_int32 *rt_dominants; /* per vif dominant gateways */
+ nbrbitmap_t rt_subordinates; /* bitmap of subordinate gateways */
+ nbrbitmap_t rt_subordadv; /* recently advertised subordinates */
+ u_int rt_timer; /* for timing out the route entry */
+ struct rtentry *rt_prev; /* link to previous entry */
+ struct gtable *rt_groups; /* link to active groups */
+};
+
+#define RTF_CHANGED 0x01 /* route changed but not reported */
+#define RTF_HOLDDOWN 0x04 /* this route is in holddown */
+
+#define ALL_ROUTES 0 /* possible arguments to report() */
+#define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */
+
+#define RT_FMT(r, s) inet_fmts((r)->rt_origin, (r)->rt_originmask, s)
diff --git a/usr.sbin/mrouted/rsrr.c b/usr.sbin/mrouted/rsrr.c
new file mode 100644
index 0000000..86d6e6e
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 1993 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation. 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 Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. 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.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+
+/* RSRR code written by Daniel Zappala, USC Information Sciences Institute,
+ * April 1995.
+ */
+
+/* May 1995 -- Added support for Route Change Notification */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#ifdef RSRR
+
+#include "defs.h"
+#include <sys/param.h>
+#ifdef HAVE_SA_LEN
+#include <stddef.h> /* for offsetof */
+#endif
+
+/*
+ * Exported variables.
+ */
+int rsrr_socket; /* interface to reservation protocol */
+
+/*
+ * Global RSRR variables.
+ */
+char rsrr_recv_buf[RSRR_MAX_LEN]; /* RSRR receive buffer */
+char rsrr_send_buf[RSRR_MAX_LEN]; /* RSRR send buffer */
+
+struct sockaddr_un client_addr;
+int client_length = sizeof(client_addr);
+
+
+/*
+ * Procedure definitions needed internally.
+ */
+static void rsrr_accept __P((int recvlen));
+static void rsrr_accept_iq __P((void));
+static int rsrr_accept_rq __P((struct rsrr_rq *route_query, int flags,
+ struct gtable *gt_notify));
+static void rsrr_read __P((int, fd_set *));
+static int rsrr_send __P((int sendlen));
+static void rsrr_cache __P((struct gtable *gt,
+ struct rsrr_rq *route_query));
+
+/* Initialize RSRR socket */
+void
+rsrr_init()
+{
+ int servlen;
+ struct sockaddr_un serv_addr;
+
+ if ((rsrr_socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
+ log(LOG_ERR, errno, "Can't create RSRR socket");
+
+ unlink(RSRR_SERV_PATH);
+ bzero((char *) &serv_addr, sizeof(serv_addr));
+ serv_addr.sun_family = AF_UNIX;
+ strcpy(serv_addr.sun_path, RSRR_SERV_PATH);
+#ifdef HAVE_SA_LEN
+ servlen = offsetof(struct sockaddr_un, sun_path) +
+ strlen(serv_addr.sun_path);
+ serv_addr.sun_len = servlen;
+#else
+ servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);
+#endif
+
+ if (bind(rsrr_socket, (struct sockaddr *) &serv_addr, servlen) < 0)
+ log(LOG_ERR, errno, "Can't bind RSRR socket");
+
+ if (register_input_handler(rsrr_socket, rsrr_read) < 0)
+ log(LOG_WARNING, 0, "Couldn't register RSRR as an input handler");
+}
+
+/* Read a message from the RSRR socket */
+static void
+rsrr_read(f, rfd)
+ int f;
+ fd_set *rfd;
+{
+ register int rsrr_recvlen;
+
+ bzero((char *) &client_addr, sizeof(client_addr));
+ rsrr_recvlen = recvfrom(rsrr_socket, rsrr_recv_buf, sizeof(rsrr_recv_buf),
+ 0, (struct sockaddr *)&client_addr, &client_length);
+ if (rsrr_recvlen < 0) {
+ if (errno != EINTR)
+ log(LOG_ERR, errno, "RSRR recvfrom");
+ return;
+ }
+ rsrr_accept(rsrr_recvlen);
+}
+
+/* Accept a message from the reservation protocol and take
+ * appropriate action.
+ */
+static void
+rsrr_accept(recvlen)
+ int recvlen;
+{
+ struct rsrr_header *rsrr;
+ struct rsrr_rq *route_query;
+
+ if (recvlen < RSRR_HEADER_LEN) {
+ log(LOG_WARNING, 0,
+ "Received RSRR packet of %d bytes, which is less than min size",
+ recvlen);
+ return;
+ }
+
+ rsrr = (struct rsrr_header *) rsrr_recv_buf;
+
+ if (rsrr->version > RSRR_MAX_VERSION) {
+ log(LOG_WARNING, 0,
+ "Received RSRR packet version %d, which I don't understand",
+ rsrr->version);
+ return;
+ }
+
+ switch (rsrr->version) {
+ case 1:
+ switch (rsrr->type) {
+ case RSRR_INITIAL_QUERY:
+ /* Send Initial Reply to client */
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Received Initial Query\n");
+ rsrr_accept_iq();
+ break;
+ case RSRR_ROUTE_QUERY:
+ /* Check size */
+ if (recvlen < RSRR_RQ_LEN) {
+ log(LOG_WARNING, 0,
+ "Received Route Query of %d bytes, which is too small",
+ recvlen);
+ break;
+ }
+ /* Get the query */
+ route_query = (struct rsrr_rq *) (rsrr_recv_buf + RSRR_HEADER_LEN);
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0,
+ "Received Route Query for src %s grp %s notification %d",
+ inet_fmt(route_query->source_addr.s_addr, s1),
+ inet_fmt(route_query->dest_addr.s_addr,s2),
+ BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT));
+ /* Send Route Reply to client */
+ rsrr_accept_rq(route_query,rsrr->flags,NULL);
+ break;
+ default:
+ log(LOG_WARNING, 0,
+ "Received RSRR packet type %d, which I don't handle",
+ rsrr->type);
+ break;
+ }
+ break;
+
+ default:
+ log(LOG_WARNING, 0,
+ "Received RSRR packet version %d, which I don't understand",
+ rsrr->version);
+ break;
+ }
+}
+
+/* Send an Initial Reply to the reservation protocol. */
+static void
+rsrr_accept_iq()
+{
+ struct rsrr_header *rsrr;
+ struct rsrr_vif *vif_list;
+ struct uvif *v;
+ int vifi, sendlen;
+
+ /* Check for space. There should be room for plenty of vifs,
+ * but we should check anyway.
+ */
+ if (numvifs > RSRR_MAX_VIFS) {
+ log(LOG_WARNING, 0,
+ "Can't send RSRR Route Reply because %d is too many vifs",
+ numvifs);
+ return;
+ }
+
+ /* Set up message */
+ rsrr = (struct rsrr_header *) rsrr_send_buf;
+ rsrr->version = 1;
+ rsrr->type = RSRR_INITIAL_REPLY;
+ rsrr->flags = 0;
+ rsrr->num = numvifs;
+
+ vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN);
+
+ /* Include the vif list. */
+ for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ vif_list[vifi].id = vifi;
+ vif_list[vifi].status = 0;
+ if (v->uv_flags & VIFF_DISABLED)
+ BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT);
+ vif_list[vifi].threshold = v->uv_threshold;
+ vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr;
+ }
+
+ /* Get the size. */
+ sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN;
+
+ /* Send it. */
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Send RSRR Initial Reply");
+ rsrr_send(sendlen);
+}
+
+/* Send a Route Reply to the reservation protocol. The Route Query
+ * contains the query to which we are responding. The flags contain
+ * the incoming flags from the query or, for route change
+ * notification, the flags that should be set for the reply. The
+ * kernel table entry contains the routing info to use for a route
+ * change notification.
+ */
+static int
+rsrr_accept_rq(route_query,flags,gt_notify)
+ struct rsrr_rq *route_query;
+ int flags;
+ struct gtable *gt_notify;
+{
+ struct rsrr_header *rsrr;
+ struct rsrr_rr *route_reply;
+ struct gtable *gt,local_g;
+ struct rtentry *r;
+ int sendlen;
+ u_long mcastgrp;
+
+ /* Set up message */
+ rsrr = (struct rsrr_header *) rsrr_send_buf;
+ rsrr->version = 1;
+ rsrr->type = RSRR_ROUTE_REPLY;
+ rsrr->flags = 0;
+ rsrr->num = 0;
+
+ route_reply = (struct rsrr_rr *) (rsrr_send_buf + RSRR_HEADER_LEN);
+ route_reply->dest_addr.s_addr = route_query->dest_addr.s_addr;
+ route_reply->source_addr.s_addr = route_query->source_addr.s_addr;
+ route_reply->query_id = route_query->query_id;
+
+ /* Blank routing entry for error. */
+ route_reply->in_vif = 0;
+ route_reply->reserved = 0;
+ route_reply->out_vif_bm = 0;
+
+ /* Get the size. */
+ sendlen = RSRR_RR_LEN;
+
+ /* If kernel table entry is defined, then we are sending a Route Reply
+ * due to a Route Change Notification event. Use the kernel table entry
+ * to supply the routing info.
+ */
+ if (gt_notify) {
+ /* Set flags */
+ rsrr->flags = flags;
+ /* Include the routing entry. */
+ route_reply->in_vif = gt_notify->gt_route->rt_parent;
+ if (BIT_TST(flags,RSRR_NOTIFICATION_BIT))
+ route_reply->out_vif_bm = gt_notify->gt_grpmems;
+ else
+ route_reply->out_vif_bm = 0;
+ } else if (find_src_grp(route_query->source_addr.s_addr, 0,
+ route_query->dest_addr.s_addr)) {
+
+ /* Found kernel entry. Code taken from add_table_entry() */
+ gt = gtp ? gtp->gt_gnext : kernel_table;
+
+ /* Include the routing entry. */
+ route_reply->in_vif = gt->gt_route->rt_parent;
+ route_reply->out_vif_bm = gt->gt_grpmems;
+
+ /* Cache reply if using route change notification. */
+ if BIT_TST(flags,RSRR_NOTIFICATION_BIT) {
+ rsrr_cache(gt,route_query);
+ BIT_SET(rsrr->flags,RSRR_NOTIFICATION_BIT);
+ }
+
+ } else {
+ /* No kernel entry; use routing table. */
+ r = determine_route(route_query->source_addr.s_addr);
+
+ if (r != NULL) {
+ /* We need to mimic what will happen if a data packet
+ * is forwarded by multicast routing -- the kernel will
+ * make an upcall and mrouted will install a route in the kernel.
+ * Our outgoing vif bitmap should reflect what that table
+ * will look like. Grab code from add_table_entry().
+ * This is gross, but it's probably better to be accurate.
+ */
+
+ gt = &local_g;
+ mcastgrp = route_query->dest_addr.s_addr;
+
+ gt->gt_mcastgrp = mcastgrp;
+ gt->gt_grpmems = 0;
+ gt->gt_scope = 0;
+ gt->gt_route = r;
+
+ /* obtain the multicast group membership list */
+ determine_forwvifs(gt);
+
+ /* Include the routing entry. */
+ route_reply->in_vif = gt->gt_route->rt_parent;
+ route_reply->out_vif_bm = gt->gt_grpmems;
+
+ } else {
+ /* Set error bit. */
+ BIT_SET(rsrr->flags,RSRR_ERROR_BIT);
+ }
+ }
+
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "%sSend RSRR Route Reply for src %s dst %s in vif %d out vif %d\n",
+ gt_notify ? "Route Change: " : "",
+ inet_fmt(route_reply->source_addr.s_addr,s1),
+ inet_fmt(route_reply->dest_addr.s_addr,s2),
+ route_reply->in_vif,route_reply->out_vif_bm);
+
+ /* Send it. */
+ return rsrr_send(sendlen);
+}
+
+/* Send an RSRR message. */
+static int
+rsrr_send(sendlen)
+ int sendlen;
+{
+ int error;
+
+ /* Send it. */
+ error = sendto(rsrr_socket, rsrr_send_buf, sendlen, 0,
+ (struct sockaddr *)&client_addr, client_length);
+
+ /* Check for errors. */
+ if (error < 0) {
+ log(LOG_WARNING, errno, "Failed send on RSRR socket");
+ } else if (error != sendlen) {
+ log(LOG_WARNING, 0,
+ "Sent only %d out of %d bytes on RSRR socket\n", error, sendlen);
+ }
+ return error;
+}
+
+/* Cache a message being sent to a client. Currently only used for
+ * caching Route Reply messages for route change notification.
+ */
+static void
+rsrr_cache(gt,route_query)
+ struct gtable *gt;
+ struct rsrr_rq *route_query;
+{
+ struct rsrr_cache *rc, **rcnp;
+ struct rsrr_header *rsrr;
+
+ rsrr = (struct rsrr_header *) rsrr_send_buf;
+
+ rcnp = &gt->gt_rsrr_cache;
+ while ((rc = *rcnp) != NULL) {
+ if ((rc->route_query.source_addr.s_addr ==
+ route_query->source_addr.s_addr) &&
+ (rc->route_query.dest_addr.s_addr ==
+ route_query->dest_addr.s_addr) &&
+ (!strcmp(rc->client_addr.sun_path,client_addr.sun_path))) {
+ /* Cache entry already exists.
+ * Check if route notification bit has been cleared.
+ */
+ if (!BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT)) {
+ /* Delete cache entry. */
+ *rcnp = rc->next;
+ free(rc);
+ } else {
+ /* Update */
+ rc->route_query.query_id = route_query->query_id;
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0,
+ "Update cached query id %ld from client %s\n",
+ rc->route_query.query_id, rc->client_addr.sun_path);
+ }
+ return;
+ }
+ rcnp = &rc->next;
+ }
+
+ /* Cache entry doesn't already exist. Create one and insert at
+ * front of list.
+ */
+ rc = (struct rsrr_cache *) malloc(sizeof(struct rsrr_cache));
+ if (rc == NULL)
+ log(LOG_ERR, 0, "ran out of memory");
+ rc->route_query.source_addr.s_addr = route_query->source_addr.s_addr;
+ rc->route_query.dest_addr.s_addr = route_query->dest_addr.s_addr;
+ rc->route_query.query_id = route_query->query_id;
+ strcpy(rc->client_addr.sun_path, client_addr.sun_path);
+ rc->client_length = client_length;
+ rc->next = gt->gt_rsrr_cache;
+ gt->gt_rsrr_cache = rc;
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Cached query id %ld from client %s\n",
+ rc->route_query.query_id,rc->client_addr.sun_path);
+}
+
+/* Send all the messages in the cache. Currently this is used to send
+ * all the cached Route Reply messages for route change notification.
+ */
+void
+rsrr_cache_send(gt,notify)
+ struct gtable *gt;
+ int notify;
+{
+ struct rsrr_cache *rc, **rcnp;
+ int flags = 0;
+
+ if (notify)
+ BIT_SET(flags,RSRR_NOTIFICATION_BIT);
+
+ rcnp = &gt->gt_rsrr_cache;
+ while ((rc = *rcnp) != NULL) {
+ if (rsrr_accept_rq(&rc->route_query,flags,gt) < 0) {
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "Deleting cached query id %ld from client %s\n",
+ rc->route_query.query_id,rc->client_addr.sun_path);
+ /* Delete cache entry. */
+ *rcnp = rc->next;
+ free(rc);
+ } else {
+ rcnp = &rc->next;
+ }
+ }
+}
+
+/* Clean the cache by deleting all entries. */
+void
+rsrr_cache_clean(gt)
+ struct gtable *gt;
+{
+ struct rsrr_cache *rc,*rc_next;
+
+ IF_DEBUG(DEBUG_RSRR)
+ log(LOG_DEBUG, 0, "cleaning cache for group %s\n",
+ inet_fmt(gt->gt_mcastgrp, s1));
+ rc = gt->gt_rsrr_cache;
+ while (rc) {
+ rc_next = rc->next;
+ free(rc);
+ rc = rc_next;
+ }
+ gt->gt_rsrr_cache = NULL;
+}
+
+void
+rsrr_clean()
+{
+ unlink(RSRR_SERV_PATH);
+}
+
+#endif /* RSRR */
diff --git a/usr.sbin/mrouted/rsrr.h b/usr.sbin/mrouted/rsrr.h
new file mode 100644
index 0000000..c88f8ce
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1993 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation. 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 Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. 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.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+
+#define RSRR_SERV_PATH "/var/run/rsrr_svr"
+/* Note this needs to be 14 chars for 4.3 BSD compatibility */
+/* Note This appears to be unused */
+#define RSRR_CLI_PATH "/var/run/rsrr_cli"
+
+#define RSRR_MAX_LEN 2048
+#define RSRR_HEADER_LEN (sizeof(struct rsrr_header))
+#define RSRR_RQ_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rq))
+#define RSRR_RR_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rr))
+#define RSRR_VIF_LEN (sizeof(struct rsrr_vif))
+
+/* Current maximum number of vifs. */
+#define RSRR_MAX_VIFS 32
+
+/* Maximum acceptable version */
+#define RSRR_MAX_VERSION 1
+
+/* RSRR message types */
+#define RSRR_ALL_TYPES 0
+#define RSRR_INITIAL_QUERY 1
+#define RSRR_INITIAL_REPLY 2
+#define RSRR_ROUTE_QUERY 3
+#define RSRR_ROUTE_REPLY 4
+
+/* RSRR Initial Reply (Vif) Status bits
+ * Each definition represents the position of the bit from right to left.
+ *
+ * Right-most bit is the disabled bit, set if the vif is administratively
+ * disabled.
+ */
+#define RSRR_DISABLED_BIT 0
+/* All other bits are zeroes */
+
+/* RSRR Route Query/Reply flag bits
+ * Each definition represents the position of the bit from right to left.
+ *
+ * Right-most bit is the Route Change Notification bit, set if the
+ * reservation protocol wishes to receive notification of
+ * a route change for the source-destination pair listed in the query.
+ * Notification is in the form of an unsolicitied Route Reply.
+ */
+#define RSRR_NOTIFICATION_BIT 0
+/* Next bit indicates an error returning the Route Reply. */
+#define RSRR_ERROR_BIT 1
+/* All other bits are zeroes */
+
+/* Definition of an RSRR message header.
+ * An Initial Query uses only the header, and an Initial Reply uses
+ * the header and a list of vifs.
+ */
+struct rsrr_header {
+ u_char version; /* RSRR Version, currently 1 */
+ u_char type; /* type of message, as defined above */
+ u_char flags; /* flags; defined by type */
+ u_char num; /* number; defined by type */
+};
+
+/* Definition of a vif as seen by the reservation protocol.
+ *
+ * Routing gives the reservation protocol a list of vifs in the
+ * Initial Reply.
+ *
+ * We explicitly list the ID because we can't assume that all routing
+ * protocols will use the same numbering scheme.
+ *
+ * The status is a bitmask of status flags, as defined above. It is the
+ * responsibility of the reservation protocol to perform any status checks
+ * if it uses the MULTICAST_VIF socket option.
+ *
+ * The threshold indicates the ttl an outgoing packet needs in order to
+ * be forwarded. The reservation protocol must perform this check itself if
+ * it uses the MULTICAST_VIF socket option.
+ *
+ * The local address is the address of the physical interface over which
+ * packets are sent.
+ */
+struct rsrr_vif {
+ u_char id; /* vif id */
+ u_char threshold; /* vif threshold ttl */
+ u_short status; /* vif status bitmask */
+ struct in_addr local_addr; /* vif local address */
+};
+
+/* Definition of an RSRR Route Query.
+ *
+ * The query asks routing for the forwarding entry for a particular
+ * source and destination. The query ID uniquely identifies the query
+ * for the reservation protocol. Thus, the combination of the client's
+ * address and the query ID forms a unique identifier for routing.
+ * Flags are defined above.
+ */
+struct rsrr_rq {
+ struct in_addr dest_addr; /* destination */
+ struct in_addr source_addr; /* source */
+ u_long query_id; /* query ID */
+};
+
+/* Definition of an RSRR Route Reply.
+ *
+ * Routing uses the reply to give the reservation protocol the
+ * forwarding entry for a source-destination pair. Routing copies the
+ * query ID from the query and fills in the incoming vif and a bitmask
+ * of the outgoing vifs.
+ * Flags are defined above.
+ */
+struct rsrr_rr {
+ struct in_addr dest_addr; /* destination */
+ struct in_addr source_addr; /* source */
+ u_long query_id; /* query ID */
+ u_short in_vif; /* incoming vif */
+ u_short reserved; /* reserved */
+ u_long out_vif_bm; /* outgoing vif bitmask */
+};
diff --git a/usr.sbin/mrouted/rsrr_var.h b/usr.sbin/mrouted/rsrr_var.h
new file mode 100644
index 0000000..9b1c09c
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr_var.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1993 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation. 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 Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. 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.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+
+/* RSRR things that are only needed by mrouted. */
+
+/* Cache of Route Query messages, distinguished by source,
+ * destination, and client addresses. Cache is flushed by RSRR client
+ * -- it sends notification when an unwanted Route Reply is received.
+ * Since this only happens during route changes, it is more likely
+ * that the cache will be flushed when the kernel table entry is
+ * deleted. */
+struct rsrr_cache {
+ struct rsrr_rq route_query; /* Cached Route Query */
+ struct sockaddr_un client_addr; /* Client address */
+ int client_length; /* Length of client */
+ struct rsrr_cache *next; /* next cache item */
+};
+
diff --git a/usr.sbin/mrouted/testrsrr/Makefile b/usr.sbin/mrouted/testrsrr/Makefile
new file mode 100644
index 0000000..131346d
--- /dev/null
+++ b/usr.sbin/mrouted/testrsrr/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+S= ${.CURDIR}/..
+.PATH: $S
+
+PROG= testrsrr
+NOMAN= #true
+SRCS= testrsrr.c
+
+CFLAGS+= -I$S
+
+install:
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/testrsrr/testrsrr.c b/usr.sbin/mrouted/testrsrr/testrsrr.c
new file mode 100644
index 0000000..6dfe831
--- /dev/null
+++ b/usr.sbin/mrouted/testrsrr/testrsrr.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright 1995 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$
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+#include <sysexits.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "rsrr.h"
+
+char sunpath[MAXPATHLEN];
+int s;
+
+void exitfn(void) {
+ close(s);
+ unlink(sunpath);
+}
+
+int main(void) {
+ struct sockaddr_un sun;
+ char buf[RSRR_MAX_LEN];
+ struct rsrr_header *rh;
+ struct rsrr_vif *rvp;
+ int i;
+
+ s = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (s < 0) {
+ err(EX_OSERR, "socket(PF_LOCAL, SOCK_DGRAM, 0)");
+ }
+
+ sun.sun_family = AF_LOCAL;
+ snprintf(sunpath, sizeof sun.sun_path, "/tmp/testrsrr.%lu",
+ (unsigned long)getpid());
+ strcpy(sun.sun_path, sunpath);
+ sun.sun_len = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(sunpath));
+
+ if (bind(s, (struct sockaddr *)&sun, sun.sun_len) < 0) {
+ err(EX_OSERR, "bind: %s", sunpath);
+ }
+
+ atexit(exitfn); /* clean up if we exit on error */
+
+ strcpy(sun.sun_path, RSRR_SERV_PATH);
+ sun.sun_len = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(sunpath));
+
+ if (connect(s, (struct sockaddr *)&sun, sun.sun_len) < 0) {
+ err(EX_OSERR, "connect: %s", RSRR_SERV_PATH);
+ }
+
+ rh = (struct rsrr_header *)buf;
+ rh->version = RSRR_MAX_VERSION;
+ rh->type = RSRR_INITIAL_QUERY;
+ rh->flags = 0;
+ rh->num = 0;
+
+ if (write(s, rh, sizeof *rh) == (ssize_t)-1) {
+ err(EX_OSERR, "write(initial query)");
+ }
+
+ if (read(s, buf, sizeof buf) == (ssize_t)-1) {
+ err(EX_OSERR, "read(initial reply)");
+ }
+
+ if (rh->version != RSRR_MAX_VERSION) {
+ errx(EX_PROTOCOL, "bad remote version %d", rh->version);
+ }
+
+ if (rh->type != RSRR_INITIAL_REPLY) {
+ errx(EX_PROTOCOL, "remote returned unexpected message type %d",
+ rh->type);
+ }
+
+ if (rh->flags) {
+ printf("confusing flags: %d\n", rh->flags);
+ }
+
+ printf("There are %d vifs configured:\n", rh->num);
+
+ printf(" Vif Thresh Status Local address\n");
+ for(i = 0, rvp = (struct rsrr_vif *)(rh + 1); i < rh->num; i++,rvp++) {
+ printf(" %3d %6d %6d %s\n", rvp->id, rvp->threshold,
+ rvp->status, inet_ntoa(rvp->local_addr));
+ }
+ exit(0);
+}
diff --git a/usr.sbin/mrouted/vif.c b/usr.sbin/mrouted/vif.c
new file mode 100644
index 0000000..423fa53
--- /dev/null
+++ b/usr.sbin/mrouted/vif.c
@@ -0,0 +1,1862 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * vif.c,v 3.8.4.56.2.1 1999/01/20 05:18:50 fenner Exp
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "defs.h"
+#include <fcntl.h>
+
+/*
+ * Exported variables.
+ */
+struct uvif uvifs[MAXVIFS]; /* array of virtual interfaces */
+vifi_t numvifs; /* number of vifs in use */
+int vifs_down; /* 1=>some interfaces are down */
+int phys_vif; /* An enabled vif */
+int udp_socket; /* Since the honkin' kernel doesn't support */
+ /* ioctls on raw IP sockets, we need a UDP */
+ /* socket as well as our IGMP (raw) socket. */
+ /* How dumb. */
+int vifs_with_neighbors; /* == 1 if I am a leaf */
+
+/*
+ * Private variables.
+ */
+struct listaddr *nbrs[MAXNBRS]; /* array of neighbors */
+
+typedef struct {
+ vifi_t vifi;
+ struct listaddr *g;
+ int q_time;
+} cbk_t;
+
+/*
+ * Forward declarations.
+ */
+static void start_vif __P((vifi_t vifi));
+static void start_vif2 __P((vifi_t vifi));
+static void stop_vif __P((vifi_t vifi));
+static void age_old_hosts __P((void));
+static void send_probe_on_vif __P((struct uvif *v));
+static void send_query __P((struct uvif *v));
+static int info_version __P((char *p, int plen));
+static void DelVif __P((void *arg));
+static int SetTimer __P((int vifi, struct listaddr *g));
+static int DeleteTimer __P((int id));
+static void SendQuery __P((void *arg));
+static int SetQueryTimer __P((struct listaddr *g, vifi_t vifi, int to_expire,
+ int q_time));
+
+
+/*
+ * Initialize the virtual interfaces, but do not install
+ * them in the kernel. Start routing on all vifs that are
+ * not down or disabled.
+ */
+void
+init_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ int enabled_vifs, enabled_phyints;
+ extern char *configfilename;
+
+ numvifs = 0;
+ vifs_with_neighbors = 0;
+ vifs_down = FALSE;
+
+ /*
+ * Configure the vifs based on the interface configuration of the
+ * the kernel and the contents of the configuration file.
+ * (Open a UDP socket for ioctl use in the config procedures if
+ * the kernel can't handle IOCTL's on the IGMP socket.)
+ */
+#ifdef IOCTL_OK_ON_RAW_SOCKET
+ udp_socket = igmp_socket;
+#else
+ if ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ log(LOG_ERR, errno, "UDP socket");
+#endif
+ log(LOG_INFO,0,"Getting vifs from kernel interfaces");
+ config_vifs_from_kernel();
+ log(LOG_INFO,0,"Getting vifs from %s",configfilename);
+ config_vifs_from_file();
+
+ /*
+ * Quit if there are fewer than two enabled vifs.
+ */
+ enabled_vifs = 0;
+ enabled_phyints = 0;
+ phys_vif = -1;
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DISABLED)) {
+ ++enabled_vifs;
+ if (!(v->uv_flags & VIFF_TUNNEL)) {
+ if (phys_vif == -1)
+ phys_vif = vifi;
+ ++enabled_phyints;
+ }
+ }
+ }
+ if (enabled_vifs < 2)
+ log(LOG_ERR, 0, "can't forward: %s",
+ enabled_vifs == 0 ? "no enabled vifs" : "only one enabled vif");
+
+ if (enabled_phyints == 0)
+ log(LOG_WARNING, 0,
+ "no enabled interfaces, forwarding via tunnels only");
+
+ log(LOG_INFO, 0, "Installing vifs in mrouted...");
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DISABLED)) {
+ if (!(v->uv_flags & VIFF_DOWN)) {
+ if (v->uv_flags & VIFF_TUNNEL)
+ log(LOG_INFO, 0, "vif #%d, tunnel %s -> %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1),
+ inet_fmt(v->uv_rmt_addr, s2));
+ else
+ log(LOG_INFO, 0, "vif #%d, phyint %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1));
+ start_vif2(vifi);
+ } else log(LOG_INFO, 0,
+ "%s is not yet up; vif #%u not in service",
+ v->uv_name, vifi);
+ }
+ }
+}
+
+/*
+ * Initialize the passed vif with all appropriate default values.
+ * "t" is true if a tunnel, or false if a phyint.
+ */
+void
+zero_vif(v, t)
+ struct uvif *v;
+ int t;
+{
+ v->uv_flags = 0;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+ v->uv_threshold = DEFAULT_THRESHOLD;
+ v->uv_rate_limit = t ? DEFAULT_TUN_RATE_LIMIT : DEFAULT_PHY_RATE_LIMIT;
+ v->uv_lcl_addr = 0;
+ v->uv_rmt_addr = 0;
+ v->uv_dst_addr = t ? 0 : dvmrp_group;
+ v->uv_subnet = 0;
+ v->uv_subnetmask = 0;
+ v->uv_subnetbcast = 0;
+ v->uv_name[0] = '\0';
+ v->uv_groups = NULL;
+ v->uv_neighbors = NULL;
+ NBRM_CLRALL(v->uv_nbrmap);
+ v->uv_querier = NULL;
+ v->uv_igmpv1_warn = 0;
+ v->uv_prune_lifetime = 0;
+ v->uv_leaf_timer = 0;
+ v->uv_acl = NULL;
+ v->uv_addrs = NULL;
+ v->uv_filter = NULL;
+ v->uv_blasterbuf = NULL;
+ v->uv_blastercur = NULL;
+ v->uv_blasterend = NULL;
+ v->uv_blasterlen = 0;
+ v->uv_blastertimer = 0;
+ v->uv_nbrup = 0;
+ v->uv_icmp_warn = 0;
+ v->uv_nroutes = 0;
+}
+
+/*
+ * Start routing on all virtual interfaces that are not down or
+ * administratively disabled.
+ */
+void
+init_installvifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+
+ log(LOG_INFO, 0, "Installing vifs in kernel...");
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & VIFF_DISABLED)) {
+ if (!(v->uv_flags & VIFF_DOWN)) {
+ if (v->uv_flags & VIFF_TUNNEL)
+ log(LOG_INFO, 0, "vif #%d, tunnel %s -> %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1),
+ inet_fmt(v->uv_rmt_addr, s2));
+ else
+ log(LOG_INFO, 0, "vif #%d, phyint %s", vifi,
+ inet_fmt(v->uv_lcl_addr, s1));
+ k_add_vif(vifi, &uvifs[vifi]);
+ } else log(LOG_INFO, 0,
+ "%s is not yet up; vif #%u not in service",
+ v->uv_name, vifi);
+ }
+ }
+}
+
+/*
+ * See if any interfaces have changed from up state to down, or vice versa,
+ * including any non-multicast-capable interfaces that are in use as local
+ * tunnel end-points. Ignore interfaces that have been administratively
+ * disabled.
+ */
+void
+check_vif_state()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ struct ifreq ifr;
+ static int checking_vifs = 0;
+
+ /*
+ * If we get an error while checking, (e.g. two interfaces go down
+ * at once, and we decide to send a prune out one of the failed ones)
+ * then don't go into an infinite loop!
+ */
+ if (checking_vifs)
+ return;
+
+ vifs_down = FALSE;
+ checking_vifs = 1;
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+
+ if (v->uv_flags & VIFF_DISABLED) continue;
+
+ strncpy(ifr.ifr_name, v->uv_name, IFNAMSIZ);
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ifr) < 0)
+ log(LOG_ERR, errno,
+ "ioctl SIOCGIFFLAGS for %s", ifr.ifr_name);
+
+ if (v->uv_flags & VIFF_DOWN) {
+ if (ifr.ifr_flags & IFF_UP) {
+ log(LOG_NOTICE, 0,
+ "%s has come up; vif #%u now in service",
+ v->uv_name, vifi);
+ v->uv_flags &= ~VIFF_DOWN;
+ start_vif(vifi);
+ }
+ else vifs_down = TRUE;
+ }
+ else {
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ log(LOG_NOTICE, 0,
+ "%s has gone down; vif #%u taken out of service",
+ v->uv_name, vifi);
+ stop_vif(vifi);
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+ }
+ }
+ checking_vifs = 0;
+}
+
+/*
+ * Send a DVMRP message on the specified vif. If DVMRP messages are
+ * to be encapsulated and sent "inside" the tunnel, use the special
+ * encapsulator. If it's not a tunnel or DVMRP messages are to be
+ * sent "beside" the tunnel, as required by earlier versions of mrouted,
+ * then just send the message.
+ */
+void
+send_on_vif(v, dst, code, datalen)
+ register struct uvif *v;
+ u_int32 dst;
+ int code;
+ int datalen;
+{
+ u_int32 group = htonl(MROUTED_LEVEL |
+ ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS));
+
+ /*
+ * The UNIX kernel will not decapsulate unicasts.
+ * Therefore, we don't send encapsulated unicasts.
+ */
+ if ((v->uv_flags & (VIFF_TUNNEL|VIFF_OTUNNEL)) == VIFF_TUNNEL &&
+ ((dst == 0) || IN_MULTICAST(ntohl(dst))))
+ send_ipip(v->uv_lcl_addr, dst ? dst : dvmrp_group, IGMP_DVMRP,
+ code, group, datalen, v);
+ else
+ send_igmp(v->uv_lcl_addr, dst ? dst : v->uv_dst_addr, IGMP_DVMRP,
+ code, group, datalen);
+}
+
+
+/*
+ * Send a probe message on vif v
+ */
+static void
+send_probe_on_vif(v)
+ register struct uvif *v;
+{
+ register char *p;
+ register int datalen = 0;
+ struct listaddr *nbr;
+ int i;
+
+ if ((v->uv_flags & VIFF_PASSIVE && v->uv_neighbors == NULL) ||
+ (v->uv_flags & VIFF_FORCE_LEAF))
+ return;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&(dvmrp_genid))[i];
+ datalen += 4;
+
+ /*
+ * add the neighbor list on the interface to the message
+ */
+ nbr = v->uv_neighbors;
+
+ while (nbr) {
+ for (i = 0; i < 4; i++)
+ *p++ = ((char *)&nbr->al_addr)[i];
+ datalen +=4;
+ nbr = nbr->al_next;
+ }
+
+ send_on_vif(v, 0, DVMRP_PROBE, datalen);
+}
+
+static void
+send_query(v)
+ register struct uvif *v;
+{
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "sending %squery on vif %d",
+ (v->uv_flags & VIFF_IGMPV1) ? "v1 " : "",
+ v - uvifs);
+ send_igmp(v->uv_lcl_addr, allhosts_group,
+ IGMP_MEMBERSHIP_QUERY,
+ (v->uv_flags & VIFF_IGMPV1) ? 0 :
+ IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
+}
+
+/*
+ * Add a vifi to the kernel and start routing on it.
+ */
+static void
+start_vif(vifi)
+ vifi_t vifi;
+{
+ /*
+ * Install the interface in the kernel's vif structure.
+ */
+ k_add_vif(vifi, &uvifs[vifi]);
+
+ start_vif2(vifi);
+}
+
+/*
+ * Add a vifi to all the user-level data structures but don't add
+ * it to the kernel yet.
+ */
+static void
+start_vif2(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v;
+ u_int32 src;
+ struct phaddr *p;
+
+ v = &uvifs[vifi];
+ src = v->uv_lcl_addr;
+
+ /*
+ * Update the existing route entries to take into account the new vif.
+ */
+ add_vif_to_routes(vifi);
+
+ if (!(v->uv_flags & VIFF_TUNNEL)) {
+ /*
+ * Join the DVMRP multicast group on the interface.
+ * (This is not strictly necessary, since the kernel promiscuously
+ * receives IGMP packets addressed to ANY IP multicast group while
+ * multicast routing is enabled. However, joining the group allows
+ * this host to receive non-IGMP packets as well, such as 'pings'.)
+ */
+ k_join(dvmrp_group, src);
+
+ /*
+ * Join the ALL-ROUTERS multicast group on the interface.
+ * This allows mtrace requests to loop back if they are run
+ * on the multicast router.
+ */
+ k_join(allrtrs_group, src);
+
+ /*
+ * Install an entry in the routing table for the subnet to which
+ * the interface is connected.
+ */
+ start_route_updates();
+ update_route(v->uv_subnet, v->uv_subnetmask, 0, 0, vifi, NULL);
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ start_route_updates();
+ update_route(p->pa_subnet, p->pa_subnetmask, 0, 0, vifi, NULL);
+ }
+
+ /*
+ * Until neighbors are discovered, assume responsibility for sending
+ * periodic group membership queries to the subnet. Send the first
+ * query.
+ */
+ v->uv_flags |= VIFF_QUERIER;
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "assuming querier duties on vif %d", vifi);
+ send_query(v);
+ }
+
+ v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
+
+ /*
+ * Send a probe via the new vif to look for neighbors.
+ */
+ send_probe_on_vif(v);
+}
+
+/*
+ * Stop routing on the specified virtual interface.
+ */
+static void
+stop_vif(vifi)
+ vifi_t vifi;
+{
+ struct uvif *v;
+ struct listaddr *a;
+ struct phaddr *p;
+
+ v = &uvifs[vifi];
+
+ if (!(v->uv_flags & VIFF_TUNNEL)) {
+ /*
+ * Depart from the DVMRP multicast group on the interface.
+ */
+ k_leave(dvmrp_group, v->uv_lcl_addr);
+
+ /*
+ * Depart from the ALL-ROUTERS multicast group on the interface.
+ */
+ k_leave(allrtrs_group, v->uv_lcl_addr);
+
+ /*
+ * Update the entry in the routing table for the subnet to which
+ * the interface is connected, to take into account the interface
+ * failure.
+ */
+ start_route_updates();
+ update_route(v->uv_subnet, v->uv_subnetmask, UNREACHABLE, 0, vifi, NULL);
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ start_route_updates();
+ update_route(p->pa_subnet, p->pa_subnetmask, UNREACHABLE, 0, vifi, NULL);
+ }
+
+ /*
+ * Discard all group addresses. (No need to tell kernel;
+ * the k_del_vif() call, below, will clean up kernel state.)
+ */
+ while (v->uv_groups != NULL) {
+ a = v->uv_groups;
+ v->uv_groups = a->al_next;
+ free((char *)a);
+ }
+
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "releasing querier duties on vif %d", vifi);
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+
+ /*
+ * Update the existing route entries to take into account the vif failure.
+ */
+ delete_vif_from_routes(vifi);
+
+ /*
+ * Delete the interface from the kernel's vif structure.
+ */
+ k_del_vif(vifi);
+
+ /*
+ * Discard all neighbor addresses.
+ */
+ if (!NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors--;
+
+ while (v->uv_neighbors != NULL) {
+ a = v->uv_neighbors;
+ v->uv_neighbors = a->al_next;
+ nbrs[a->al_index] = NULL;
+ free((char *)a);
+ }
+ NBRM_CLRALL(v->uv_nbrmap);
+}
+
+
+/*
+ * stop routing on all vifs
+ */
+void
+stop_all_vifs()
+{
+ vifi_t vifi;
+ struct uvif *v;
+ struct listaddr *a;
+ struct vif_acl *acl;
+
+ for (vifi = 0; vifi < numvifs; vifi++) {
+ v = &uvifs[vifi];
+ while (v->uv_groups != NULL) {
+ a = v->uv_groups;
+ v->uv_groups = a->al_next;
+ free((char *)a);
+ }
+ while (v->uv_neighbors != NULL) {
+ a = v->uv_neighbors;
+ v->uv_neighbors = a->al_next;
+ nbrs[a->al_index] = NULL;
+ free((char *)a);
+ }
+ while (v->uv_acl != NULL) {
+ acl = v->uv_acl;
+ v->uv_acl = acl->acl_next;
+ free((char *)acl);
+ }
+ }
+}
+
+
+/*
+ * Find the virtual interface from which an incoming packet arrived,
+ * based on the packet's source and destination IP addresses.
+ */
+vifi_t
+find_vif(src, dst)
+ register u_int32 src;
+ register u_int32 dst;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct phaddr *p;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+ if (v->uv_flags & VIFF_TUNNEL) {
+ if (src == v->uv_rmt_addr && (dst == v->uv_lcl_addr ||
+ dst == dvmrp_group))
+ return(vifi);
+ }
+ else {
+ if ((src & v->uv_subnetmask) == v->uv_subnet &&
+ ((v->uv_subnetmask == 0xffffffff) ||
+ (src != v->uv_subnetbcast)))
+ return(vifi);
+ for (p=v->uv_addrs; p; p=p->pa_next) {
+ if ((src & p->pa_subnetmask) == p->pa_subnet &&
+ ((p->pa_subnetmask == 0xffffffff) ||
+ (src != p->pa_subnetbcast)))
+ return(vifi);
+ }
+ }
+ }
+ }
+ return (NO_VIF);
+}
+
+static void
+age_old_hosts()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+
+ /*
+ * Decrement the old-hosts-present timer for each
+ * active group on each vif.
+ */
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++)
+ for (g = v->uv_groups; g != NULL; g = g->al_next)
+ if (g->al_old)
+ g->al_old--;
+}
+
+
+/*
+ * Send group membership queries on each interface for which I am querier.
+ * Note that technically, there should be a timer per interface, as the
+ * dynamics of querier election can cause the "right" time to send a
+ * query to be different on different interfaces. However, this simple
+ * implementation only ever sends queries sooner than the "right" time,
+ * so can not cause loss of membership (but can send more packets than
+ * necessary)
+ */
+void
+query_groups()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ if (v->uv_flags & VIFF_QUERIER) {
+ send_query(v);
+ }
+ }
+ age_old_hosts();
+}
+
+/*
+ * Process an incoming host membership query. Warn about
+ * IGMP version mismatches, perform querier election, and
+ * handle group-specific queries when we're not the querier.
+ */
+void
+accept_membership_query(src, dst, group, tmo)
+ u_int32 src, dst, group;
+ int tmo;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF ||
+ (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+ log(LOG_INFO, 0,
+ "ignoring group membership query from non-adjacent host %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ v = &uvifs[vifi];
+
+ if ((tmo == 0 && !(v->uv_flags & VIFF_IGMPV1)) ||
+ (tmo != 0 && (v->uv_flags & VIFF_IGMPV1))) {
+ int i;
+
+ /*
+ * Exponentially back-off warning rate
+ */
+ i = ++v->uv_igmpv1_warn;
+ while (i && !(i & 1))
+ i >>= 1;
+ if (i == 1)
+ log(LOG_WARNING, 0, "%s %s on vif %d, %s",
+ tmo == 0 ? "Received IGMPv1 report from"
+ : "Received IGMPv2 report from",
+ inet_fmt(src, s1),
+ vifi,
+ tmo == 0 ? "please configure vif for IGMPv1"
+ : "but I am configured for IGMPv1");
+ }
+
+ if (v->uv_querier == NULL || v->uv_querier->al_addr != src) {
+ /*
+ * This might be:
+ * - A query from a new querier, with a lower source address
+ * than the current querier (who might be me)
+ * - A query from a new router that just started up and doesn't
+ * know who the querier is.
+ */
+ if (ntohl(src) < (v->uv_querier ? ntohl(v->uv_querier->al_addr)
+ : ntohl(v->uv_lcl_addr))) {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d",
+ inet_fmt(src, s1),
+ v->uv_querier ? inet_fmt(v->uv_querier->al_addr, s2) :
+ "me", vifi);
+ if (!v->uv_querier) {
+ v->uv_querier = (struct listaddr *)
+ malloc(sizeof(struct listaddr));
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+ time(&v->uv_querier->al_ctime);
+ v->uv_querier->al_addr = src;
+ } else {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "ignoring query from %s; querier on vif %d is still %s",
+ inet_fmt(src, s1), vifi,
+ v->uv_querier ? inet_fmt(v->uv_querier->al_addr, s2) :
+ "me");
+
+ return;
+ }
+ }
+
+ /*
+ * Reset the timer since we've received a query.
+ */
+ if (v->uv_querier && src == v->uv_querier->al_addr)
+ v->uv_querier->al_timer = 0;
+
+ /*
+ * If this is a Group-Specific query which we did not source,
+ * we must set our membership timer to [Last Member Query Count] *
+ * the [Max Response Time] in the packet.
+ */
+ if (!(v->uv_flags & (VIFF_IGMPV1|VIFF_QUERIER)) && group != 0 &&
+ src != v->uv_lcl_addr) {
+ register struct listaddr *g;
+
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "%s for %s from %s on vif %d, timer %d",
+ "Group-specific membership query",
+ inet_fmt(group, s2), inet_fmt(src, s1), vifi, tmo);
+
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (group == g->al_addr && g->al_query == 0) {
+ /* setup a timeout to remove the group membership */
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT *
+ tmo / IGMP_TIMER_SCALE;
+ /* use al_query to record our presence in last-member state */
+ g->al_query = -1;
+ g->al_timerid = SetTimer(vifi, g);
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "timer for grp %s on vif %d set to %d",
+ inet_fmt(group, s2), vifi, g->al_timer);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Process an incoming group membership report.
+ */
+void
+accept_group_report(src, dst, group, r_type)
+ u_int32 src, dst, group;
+ int r_type;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF ||
+ (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+ log(LOG_INFO, 0,
+ "ignoring group membership report from non-adjacent host %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ v = &uvifs[vifi];
+
+ /*
+ * Look for the group in our group list; if found, reset its timer.
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (group == g->al_addr) {
+ if (r_type == IGMP_V1_MEMBERSHIP_REPORT)
+ g->al_old = OLD_AGE_THRESHOLD;
+
+ g->al_reporter = src;
+
+ /** delete old timers, set a timer for expiration **/
+ g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
+ if (g->al_query)
+ g->al_query = DeleteTimer(g->al_query);
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+ g->al_timerid = SetTimer(vifi, g);
+ break;
+ }
+ }
+
+ /*
+ * If not found, add it to the list and update kernel cache.
+ */
+ if (g == NULL) {
+ g = (struct listaddr *)malloc(sizeof(struct listaddr));
+ if (g == NULL)
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+
+ g->al_addr = group;
+ if (r_type == IGMP_V1_MEMBERSHIP_REPORT)
+ g->al_old = OLD_AGE_THRESHOLD;
+ else
+ g->al_old = 0;
+
+ /** set a timer for expiration **/
+ g->al_query = 0;
+ g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
+ g->al_reporter = src;
+ g->al_timerid = SetTimer(vifi, g);
+ g->al_next = v->uv_groups;
+ v->uv_groups = g;
+ time(&g->al_ctime);
+
+ update_lclgrp(vifi, group);
+ }
+
+ /*
+ * Check if a graft is necessary for this group
+ */
+ chkgrp_graft(vifi, group);
+}
+
+/*
+ * Process an incoming IGMPv2 Leave Group message.
+ */
+void
+accept_leave_message(src, dst, group)
+ u_int32 src, dst, group;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *g;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF ||
+ (uvifs[vifi].uv_flags & VIFF_TUNNEL)) {
+ log(LOG_INFO, 0,
+ "ignoring group leave report from non-adjacent host %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ v = &uvifs[vifi];
+
+ if (!(v->uv_flags & VIFF_QUERIER) || (v->uv_flags & VIFF_IGMPV1))
+ return;
+
+ /*
+ * Look for the group in our group list in order to set up a short-timeout
+ * query.
+ */
+ for (g = v->uv_groups; g != NULL; g = g->al_next) {
+ if (group == g->al_addr) {
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0,
+ "[vif.c, _accept_leave_message] %d %d \n",
+ g->al_old, g->al_query);
+
+ /* Ignore the leave message if there are old hosts present */
+ if (g->al_old)
+ return;
+
+ /* still waiting for a reply to a query, ignore the leave */
+ if (g->al_query)
+ return;
+
+ /** delete old timer set a timer for expiration **/
+ if (g->al_timerid)
+ g->al_timerid = DeleteTimer(g->al_timerid);
+
+#if IGMP_LAST_MEMBER_QUERY_COUNT != 2
+This code needs to be updated to keep a counter of the number
+of queries remaining.
+#endif
+ /** send a group specific querry **/
+ g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL *
+ (IGMP_LAST_MEMBER_QUERY_COUNT + 1);
+ send_igmp(v->uv_lcl_addr, g->al_addr,
+ IGMP_MEMBERSHIP_QUERY,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE,
+ g->al_addr, 0);
+ g->al_query = SetQueryTimer(g, vifi,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL,
+ IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE);
+ g->al_timerid = SetTimer(vifi, g);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Send a periodic probe on all vifs.
+ * Useful to determine one-way interfaces.
+ * Detect neighbor loss faster.
+ */
+void
+probe_for_neighbors()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ if (!(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+ send_probe_on_vif(v);
+ }
+ }
+}
+
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void
+accept_neighbor_request(src, dst)
+ u_int32 src, dst;
+{
+ vifi_t vifi;
+ struct uvif *v;
+ u_char *p, *ncount;
+ struct listaddr *la;
+ int datalen;
+ u_int32 temp_addr, them = src;
+
+#define PUT_ADDR(a) temp_addr = ntohl(a); \
+ *p++ = temp_addr >> 24; \
+ *p++ = (temp_addr >> 16) & 0xFF; \
+ *p++ = (temp_addr >> 8) & 0xFF; \
+ *p++ = temp_addr & 0xFF;
+
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ if (v->uv_flags & VIFF_DISABLED)
+ continue;
+
+ ncount = 0;
+
+ for (la = v->uv_neighbors; la; la = la->al_next) {
+
+ /* Make sure that there's room for this neighbor... */
+ if (datalen + (ncount == 0 ? 4 + 3 + 4 : 4) > MAX_DVMRP_DATA_LEN) {
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+ htonl(MROUTED_LEVEL), datalen);
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+ ncount = 0;
+ }
+
+ /* Put out the header for this neighbor list... */
+ if (ncount == 0) {
+ PUT_ADDR(v->uv_lcl_addr);
+ *p++ = v->uv_metric;
+ *p++ = v->uv_threshold;
+ ncount = p;
+ *p++ = 0;
+ datalen += 4 + 3;
+ }
+
+ PUT_ADDR(la->al_addr);
+ datalen += 4;
+ (*ncount)++;
+ }
+ }
+
+ if (datalen != 0)
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS,
+ htonl(MROUTED_LEVEL), datalen);
+}
+
+/*
+ * Send a list of all of our neighbors to the requestor, `src'.
+ */
+void
+accept_neighbor_request2(src, dst)
+ u_int32 src, dst;
+{
+ vifi_t vifi;
+ struct uvif *v;
+ u_char *p, *ncount;
+ struct listaddr *la;
+ int datalen;
+ u_int32 them = src;
+
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ register u_short vflags = v->uv_flags;
+ register u_char rflags = 0;
+ if (vflags & VIFF_TUNNEL)
+ rflags |= DVMRP_NF_TUNNEL;
+ if (vflags & VIFF_SRCRT)
+ rflags |= DVMRP_NF_SRCRT;
+ if (vflags & VIFF_DOWN)
+ rflags |= DVMRP_NF_DOWN;
+ if (vflags & VIFF_DISABLED)
+ rflags |= DVMRP_NF_DISABLED;
+ if (vflags & VIFF_QUERIER)
+ rflags |= DVMRP_NF_QUERIER;
+ if (vflags & VIFF_LEAF)
+ rflags |= DVMRP_NF_LEAF;
+ ncount = 0;
+ la = v->uv_neighbors;
+ if (la == NULL) {
+ /*
+ * include down & disabled interfaces and interfaces on
+ * leaf nets.
+ */
+ if (rflags & DVMRP_NF_TUNNEL)
+ rflags |= DVMRP_NF_DOWN;
+ if (datalen > MAX_DVMRP_DATA_LEN - 12) {
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), datalen);
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+ }
+ *(u_int*)p = v->uv_lcl_addr;
+ p += 4;
+ *p++ = v->uv_metric;
+ *p++ = v->uv_threshold;
+ *p++ = rflags;
+ *p++ = 1;
+ *(u_int*)p = v->uv_rmt_addr;
+ p += 4;
+ datalen += 12;
+ } else {
+ for ( ; la; la = la->al_next) {
+ /* Make sure that there's room for this neighbor... */
+ if (datalen + (ncount == 0 ? 4+4+4 : 4) > MAX_DVMRP_DATA_LEN) {
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), datalen);
+ p = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+ datalen = 0;
+ ncount = 0;
+ }
+ /* Put out the header for this neighbor list... */
+ if (ncount == 0) {
+ /* If it's a one-way tunnel, mark it down. */
+ if (rflags & DVMRP_NF_TUNNEL && la->al_flags & NBRF_ONEWAY)
+ rflags |= DVMRP_NF_DOWN;
+ *(u_int*)p = v->uv_lcl_addr;
+ p += 4;
+ *p++ = v->uv_metric;
+ *p++ = v->uv_threshold;
+ *p++ = rflags;
+ ncount = p;
+ *p++ = 0;
+ datalen += 4 + 4;
+ }
+ /* Don't report one-way peering on phyint at all */
+ if (!(rflags & DVMRP_NF_TUNNEL) && la->al_flags & NBRF_ONEWAY)
+ continue;
+ *(u_int*)p = la->al_addr;
+ p += 4;
+ datalen += 4;
+ (*ncount)++;
+ }
+ if (*ncount == 0) {
+ *(u_int*)p = v->uv_rmt_addr;
+ p += 4;
+ datalen += 4;
+ (*ncount)++;
+ }
+ }
+ }
+ if (datalen != 0)
+ send_igmp(INADDR_ANY, them, IGMP_DVMRP, DVMRP_NEIGHBORS2,
+ htonl(MROUTED_LEVEL), datalen);
+}
+
+void
+accept_info_request(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+ u_char *q;
+ int len;
+ int outlen = 0;
+
+ q = (u_char *) (send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN);
+
+ /* To be general, this must deal properly with breaking up over-sized
+ * packets. That implies passing a length to each function, and
+ * allowing each function to request to be called again. Right now,
+ * we're only implementing the one thing we are positive will fit into
+ * a single packet, so we wimp out.
+ */
+ while (datalen > 0) {
+ len = 0;
+ switch (*p) {
+ case DVMRP_INFO_VERSION:
+ len = info_version(q, RECV_BUF_SIZE-(q-(u_char *)send_buf));
+ break;
+
+ case DVMRP_INFO_NEIGHBORS:
+ default:
+ log(LOG_INFO, 0, "ignoring unknown info type %d", *p);
+ break;
+ }
+ *(q+1) = len++;
+ outlen += len * 4;
+ q += len * 4;
+ len = (*(p+1) + 1) * 4;
+ p += len;
+ datalen -= len;
+ }
+
+ if (outlen != 0)
+ send_igmp(INADDR_ANY, src, IGMP_DVMRP, DVMRP_INFO_REPLY,
+ htonl(MROUTED_LEVEL), outlen);
+}
+
+/*
+ * Information response -- return version string
+ */
+static int
+info_version(p, plen)
+ char *p;
+ int plen;
+{
+ int len;
+ extern char versionstring[];
+
+ *p++ = DVMRP_INFO_VERSION;
+ p++; /* skip over length */
+ *p++ = 0; /* zero out */
+ *p++ = 0; /* reserved fields */
+ strncpy(p, versionstring, plen - 4);
+ p[plen-5] = '\0';
+
+ len = strlen(versionstring);
+ return ((len + 3) / 4);
+}
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void
+accept_neighbors(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ log(LOG_INFO, 0, "ignoring spurious DVMRP neighbor list from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Process an incoming neighbor-list message.
+ */
+void
+accept_neighbors2(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+ IF_DEBUG(DEBUG_PKT)
+ log(LOG_DEBUG, 0, "ignoring spurious DVMRP neighbor list2 from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+/*
+ * Process an incoming info reply message.
+ */
+void
+accept_info_reply(src, dst, p, datalen)
+ u_int32 src, dst;
+ u_char *p;
+ int datalen;
+{
+ IF_DEBUG(DEBUG_PKT)
+ log(LOG_DEBUG, 0, "ignoring spurious DVMRP info reply from %s to %s",
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
+
+
+/*
+ * Update the neighbor entry for neighbor 'addr' on vif 'vifi'.
+ * 'msgtype' is the type of DVMRP message received from the neighbor.
+ * Return the neighbor entry if 'addr' is a valid neighbor, FALSE otherwise.
+ */
+struct listaddr *
+update_neighbor(vifi, addr, msgtype, p, datalen, level)
+ vifi_t vifi;
+ u_int32 addr;
+ int msgtype;
+ char *p;
+ int datalen;
+ u_int32 level;
+{
+ register struct uvif *v;
+ register struct listaddr *n;
+ int pv = level & 0xff;
+ int mv = (level >> 8) & 0xff;
+ int has_genid = 0;
+ int in_router_list = 0;
+ int dvmrpspec = 0;
+ u_int32 genid;
+ u_int32 send_tables = 0;
+ int i;
+ int do_reset = FALSE;
+
+ v = &uvifs[vifi];
+
+ /*
+ * Confirm that 'addr' is a valid neighbor address on vif 'vifi'.
+ * IT IS ASSUMED that this was preceded by a call to find_vif(), which
+ * checks that 'addr' is either a valid remote tunnel endpoint or a
+ * non-broadcast address belonging to a directly-connected subnet.
+ * Therefore, here we check only that 'addr' is not our own address
+ * (due to an impostor or erroneous loopback) or an address of the form
+ * {subnet,0} ("the unknown host"). These checks are not performed in
+ * find_vif() because those types of address are acceptable for some
+ * types of IGMP message (such as group membership reports).
+ */
+ if (!(v->uv_flags & VIFF_TUNNEL) &&
+ (addr == v->uv_lcl_addr ||
+ addr == v->uv_subnet )) {
+ log(LOG_WARNING, 0,
+ "received DVMRP message from %s: %s",
+ (addr == v->uv_lcl_addr) ? "self (check device loopback)" :
+ "'the unknown host'",
+ inet_fmt(addr, s1));
+ return NULL;
+ }
+
+ /*
+ * Ignore all neighbors on vifs forced into leaf mode
+ */
+ if (v->uv_flags & VIFF_FORCE_LEAF) {
+ return NULL;
+ }
+
+ /*
+ * mrouted's version 3.3 and later include the generation ID
+ * and the list of neighbors on the vif in their probe messages.
+ */
+ if (msgtype == DVMRP_PROBE && ((pv == 3 && mv > 2) ||
+ (pv > 3 && pv < 10))) {
+ u_int32 router;
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "checking probe from %s (%d.%d) on vif %d",
+ inet_fmt(addr, s1), pv, mv, vifi);
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return NULL;
+ }
+
+ has_genid = 1;
+
+ for (i = 0; i < 4; i++)
+ ((char *)&genid)[i] = *p++;
+ datalen -= 4;
+
+ while (datalen > 0) {
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return NULL;
+ }
+ for (i = 0; i < 4; i++)
+ ((char *)&router)[i] = *p++;
+ datalen -= 4;
+
+ if (router == v->uv_lcl_addr) {
+ in_router_list = 1;
+ break;
+ }
+ }
+ }
+
+ if ((pv == 3 && mv == 255) || (pv > 3 && pv < 10))
+ dvmrpspec = 1;
+
+ /*
+ * Look for addr in list of neighbors.
+ */
+ for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
+ if (addr == n->al_addr) {
+ break;
+ }
+ }
+
+ if (n == NULL) {
+ /*
+ * New neighbor.
+ *
+ * If this neighbor follows the DVMRP spec, start the probe
+ * handshake. If not, then it doesn't require the probe
+ * handshake, so establish the peering immediately.
+ */
+ if (dvmrpspec && (msgtype != DVMRP_PROBE))
+ return NULL;
+
+ for (i = 0; i < MAXNBRS; i++)
+ if (nbrs[i] == NULL)
+ break;
+
+ if (i == MAXNBRS) {
+ /* XXX This is a severe new restriction. */
+ /* XXX want extensible bitmaps! */
+ log(LOG_ERR, 0, "Can't handle %dth neighbor %s on vif %d!",
+ MAXNBRS, inet_fmt(addr, s1), vifi);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * Add it to our list of neighbors.
+ */
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x idx %d",
+ inet_fmt(addr, s1), vifi, level & 0xff, (level >> 8) & 0xff,
+ (level >> 16) & 0xff, i);
+
+ n = (struct listaddr *)malloc(sizeof(struct listaddr));
+ if (n == NULL)
+ log(LOG_ERR, 0, "ran out of memory"); /* fatal */
+
+ n->al_addr = addr;
+ n->al_pv = pv;
+ n->al_mv = mv;
+ n->al_genid = has_genid ? genid : 0;
+ n->al_index = i;
+ nbrs[i] = n;
+
+ time(&n->al_ctime);
+ n->al_timer = 0;
+ n->al_flags = has_genid ? NBRF_GENID : 0;
+ n->al_next = v->uv_neighbors;
+ v->uv_neighbors = n;
+
+ /*
+ * If we are not configured to peer with non-pruning routers,
+ * check the deprecated "I-know-how-to-prune" bit. This bit
+ * was MBZ in early mrouted implementations (<3.5) and is required
+ * to be set by the DVMRPv3 specification.
+ */
+ if (!(v->uv_flags & VIFF_ALLOW_NONPRUNERS) &&
+ !((level & 0x020000) || (pv == 3 && mv < 5))) {
+ n->al_flags |= NBRF_TOOOLD;
+ }
+
+ /*
+ * If this router implements the DVMRPv3 spec, then don't peer
+ * with him if we haven't yet established a bidirectional connection.
+ */
+ if (dvmrpspec) {
+ if (!in_router_list) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "waiting for probe from %s with my addr",
+ inet_fmt(addr, s1));
+ n->al_flags |= NBRF_WAITING;
+ return NULL;
+ }
+ }
+
+ if (n->al_flags & NBRF_DONTPEER) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "not peering with %s on vif %d because %x",
+ inet_fmt(addr, s1), vifi, n->al_flags & NBRF_DONTPEER);
+ return NULL;
+ }
+
+ /*
+ * If we thought that we had no neighbors on this vif, send a route
+ * report to the vif. If this is just a new neighbor on the same
+ * vif, send the route report just to the new neighbor.
+ */
+ if (NBRM_ISEMPTY(v->uv_nbrmap)) {
+ send_tables = v->uv_dst_addr;
+ vifs_with_neighbors++;
+ } else {
+ send_tables = addr;
+ }
+
+
+ NBRM_SET(i, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, i);
+ } else {
+ /*
+ * Found it. Reset its timer.
+ */
+ n->al_timer = 0;
+
+ if (n->al_flags & NBRF_WAITING && msgtype == DVMRP_PROBE) {
+ n->al_flags &= ~NBRF_WAITING;
+ if (!in_router_list) {
+ log(LOG_WARNING, 0, "possible one-way peering with %s on vif %d",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags |= NBRF_ONEWAY;
+ return NULL;
+ } else {
+ if (NBRM_ISEMPTY(v->uv_nbrmap)) {
+ send_tables = v->uv_dst_addr;
+ vifs_with_neighbors++;
+ } else {
+ send_tables = addr;
+ }
+ NBRM_SET(n->al_index, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, n->al_index);
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "%s on vif %d exits WAITING",
+ inet_fmt(addr, s1), vifi);
+ }
+ }
+
+ if (n->al_flags & NBRF_ONEWAY && msgtype == DVMRP_PROBE) {
+ if (in_router_list) {
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors++;
+ NBRM_SET(n->al_index, v->uv_nbrmap);
+ add_neighbor_to_routes(vifi, n->al_index);
+ log(LOG_NOTICE, 0, "peering with %s on vif %d is no longer one-way",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags &= ~NBRF_ONEWAY;
+ } else {
+ /* XXX rate-limited warning message? */
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "%s on vif %d is still ONEWAY",
+ inet_fmt(addr, s1), vifi);
+ }
+ }
+
+ /*
+ * When peering with a genid-capable but pre-DVMRP spec peer,
+ * we might bring up the peering with a route report and not
+ * remember his genid. Assume that he doesn't send a route
+ * report and then reboot before sending a probe.
+ */
+ if (has_genid && !(n->al_flags & NBRF_GENID)) {
+ n->al_flags |= NBRF_GENID;
+ n->al_genid = genid;
+ }
+
+ /*
+ * update the neighbors version and protocol number and genid
+ * if changed => router went down and came up,
+ * so take action immediately.
+ */
+ if ((n->al_pv != pv) ||
+ (n->al_mv != mv) ||
+ (has_genid && n->al_genid != genid)) {
+
+ do_reset = TRUE;
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0,
+ "version/genid change neighbor %s [old:%d.%d/%8x, new:%d.%d/%8x]",
+ inet_fmt(addr, s1),
+ n->al_pv, n->al_mv, n->al_genid, pv, mv, genid);
+
+ n->al_pv = pv;
+ n->al_mv = mv;
+ n->al_genid = genid;
+ time(&n->al_ctime);
+ }
+
+ if ((pv == 3 && mv > 2) || (pv > 3 && pv < 10)) {
+ if (!(n->al_flags & VIFF_ONEWAY) && has_genid && !in_router_list &&
+ (time(NULL) - n->al_ctime > 20)) {
+ if (NBRM_ISSET(n->al_index, v->uv_nbrmap)) {
+ NBRM_CLR(n->al_index, v->uv_nbrmap);
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors--;
+ }
+ delete_neighbor_from_routes(addr, vifi, n->al_index);
+ reset_neighbor_state(vifi, addr);
+ log(LOG_WARNING, 0, "peering with %s on vif %d is one-way",
+ inet_fmt(addr, s1), vifi);
+ n->al_flags |= NBRF_ONEWAY;
+ }
+ }
+
+ if (n->al_flags & NBRF_DONTPEER) {
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "not peering with %s on vif %d because %x",
+ inet_fmt(addr, s1), vifi, n->al_flags & NBRF_DONTPEER);
+ return NULL;
+ }
+
+ /* check "leaf" flag */
+ }
+ if (do_reset) {
+ reset_neighbor_state(vifi, addr);
+ if (!send_tables)
+ send_tables = addr;
+ }
+ if (send_tables) {
+ send_probe_on_vif(v);
+ report(ALL_ROUTES, vifi, send_tables);
+ }
+ v->uv_leaf_timer = 0;
+ v->uv_flags &= ~VIFF_LEAF;
+
+ return n;
+}
+
+
+/*
+ * On every timer interrupt, advance the timer in each neighbor and
+ * group entry on every vif.
+ */
+void
+age_vifs()
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *a, *prev_a;
+ register u_int32 addr;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v ) {
+ if (v->uv_leaf_timer && (v->uv_leaf_timer -= TIMER_INTERVAL == 0)) {
+ v->uv_flags |= VIFF_LEAF;
+ }
+
+ for (prev_a = (struct listaddr *)&(v->uv_neighbors),
+ a = v->uv_neighbors;
+ a != NULL;
+ prev_a = a, a = a->al_next) {
+ int exp_time;
+ int idx;
+
+ if (((a->al_pv == 3) && (a->al_mv >= 3)) ||
+ ((a->al_pv > 3) && (a->al_pv < 10)))
+ exp_time = NEIGHBOR_EXPIRE_TIME;
+ else
+ exp_time = OLD_NEIGHBOR_EXPIRE_TIME;
+
+ if ((a->al_timer += TIMER_INTERVAL) < exp_time)
+ continue;
+
+ IF_DEBUG(DEBUG_PEER)
+ log(LOG_DEBUG, 0, "Neighbor %s (%d.%d) expired after %d seconds",
+ inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv, exp_time);
+
+ /*
+ * Neighbor has expired; delete it from the neighbor list,
+ * delete it from the 'dominants' and 'subordinates arrays of
+ * any route entries.
+ */
+ NBRM_CLR(a->al_index, v->uv_nbrmap);
+ nbrs[a->al_index] = NULL; /* XXX is it a good idea to reuse indxs? */
+ idx = a->al_index;
+ addr = a->al_addr;
+ prev_a->al_next = a->al_next;
+ free((char *)a);
+ a = prev_a;/*XXX use ** */
+
+ delete_neighbor_from_routes(addr, vifi, idx);
+ reset_neighbor_state(vifi, addr);
+
+ if (NBRM_ISEMPTY(v->uv_nbrmap))
+ vifs_with_neighbors--;
+
+ v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
+ }
+
+ if (v->uv_querier &&
+ (v->uv_querier->al_timer += TIMER_INTERVAL) >
+ IGMP_OTHER_QUERIER_PRESENT_INTERVAL) {
+ /*
+ * The current querier has timed out. We must become the
+ * querier.
+ */
+ IF_DEBUG(DEBUG_IGMP)
+ log(LOG_DEBUG, 0, "querier %s timed out",
+ inet_fmt(v->uv_querier->al_addr, s1));
+ free(v->uv_querier);
+ v->uv_querier = NULL;
+ v->uv_flags |= VIFF_QUERIER;
+ send_query(v);
+ }
+ }
+}
+
+/*
+ * Returns the neighbor info struct for a given neighbor
+ */
+struct listaddr *
+neighbor_info(vifi, addr)
+ vifi_t vifi;
+ u_int32 addr;
+{
+ struct listaddr *u;
+
+ for (u = uvifs[vifi].uv_neighbors; u; u = u->al_next)
+ if (u->al_addr == addr)
+ return u;
+
+ return NULL;
+}
+
+static struct vnflags {
+ int vn_flag;
+ char *vn_name;
+} vifflags[] = {
+ { VIFF_DOWN, "down" },
+ { VIFF_DISABLED, "disabled" },
+ { VIFF_QUERIER, "querier" },
+ { VIFF_ONEWAY, "one-way" },
+ { VIFF_LEAF, "leaf" },
+ { VIFF_IGMPV1, "IGMPv1" },
+ { VIFF_REXMIT_PRUNES, "rexmit_prunes" },
+ { VIFF_PASSIVE, "passive" },
+ { VIFF_ALLOW_NONPRUNERS,"allow_nonpruners" },
+ { VIFF_NOFLOOD, "noflood" },
+ { VIFF_NOTRANSIT, "notransit" },
+ { VIFF_BLASTER, "blaster" },
+ { VIFF_FORCE_LEAF, "force_leaf" },
+ { VIFF_OTUNNEL, "old-tunnel" },
+};
+
+static struct vnflags nbrflags[] = {
+ { NBRF_LEAF, "leaf" },
+ { NBRF_GENID, "have-genid" },
+ { NBRF_WAITING, "waiting" },
+ { NBRF_ONEWAY, "one-way" },
+ { NBRF_TOOOLD, "too old" },
+ { NBRF_TOOMANYROUTES, "too many routes" },
+ { NBRF_NOTPRUNING, "not pruning?" },
+};
+
+/*
+ * Print the contents of the uvifs array on file 'fp'.
+ */
+void
+dump_vifs(fp)
+ FILE *fp;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ register struct listaddr *a;
+ register struct phaddr *p;
+ register struct vif_acl *acl;
+ int i;
+ struct sioc_vif_req v_req;
+ time_t now;
+ char *label;
+
+ time(&now);
+ fprintf(fp, "vifs_with_neighbors = %d\n", vifs_with_neighbors);
+
+ if (vifs_with_neighbors == 1)
+ fprintf(fp,"[This host is a leaf]\n\n");
+
+ fprintf(fp,
+ "\nVirtual Interface Table\n%s",
+ "Vif Name Local-Address ");
+ fprintf(fp,
+ "M Thr Rate Flags\n");
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; vifi++, v++) {
+
+ fprintf(fp, "%2u %6s %-15s %6s: %-18s %2u %3u %5u ",
+ vifi,
+ v->uv_name,
+ inet_fmt(v->uv_lcl_addr, s1),
+ (v->uv_flags & VIFF_TUNNEL) ?
+ "tunnel":
+ "subnet",
+ (v->uv_flags & VIFF_TUNNEL) ?
+ inet_fmt(v->uv_rmt_addr, s2) :
+ inet_fmts(v->uv_subnet, v->uv_subnetmask, s3),
+ v->uv_metric,
+ v->uv_threshold,
+ v->uv_rate_limit);
+
+ for (i = 0; i < sizeof(vifflags) / sizeof(struct vnflags); i++)
+ if (v->uv_flags & vifflags[i].vn_flag)
+ fprintf(fp, " %s", vifflags[i].vn_name);
+
+ fprintf(fp, "\n");
+ /*
+ fprintf(fp, " #routes: %d\n", v->uv_nroutes);
+ */
+ if (v->uv_admetric != 0)
+ fprintf(fp, " advert-metric %2u\n",
+ v->uv_admetric);
+
+ label = "alternate subnets:";
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ fprintf(fp, " %18s %s\n", label,
+ inet_fmts(p->pa_subnet, p->pa_subnetmask, s1));
+ label = "";
+ }
+
+ label = "peers:";
+ for (a = v->uv_neighbors; a != NULL; a = a->al_next) {
+ fprintf(fp, " %6s %s (%d.%d) [%d]",
+ label, inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv,
+ a->al_index);
+ for (i = 0; i < sizeof(nbrflags) / sizeof(struct vnflags); i++)
+ if (a->al_flags & nbrflags[i].vn_flag)
+ fprintf(fp, " %s", nbrflags[i].vn_name);
+ fprintf(fp, " up %s\n", scaletime(now - a->al_ctime));
+ /*fprintf(fp, " #routes %d\n", a->al_nroutes);*/
+ label = "";
+ }
+
+ label = "group host (time left):";
+ for (a = v->uv_groups; a != NULL; a = a->al_next) {
+ fprintf(fp, " %23s %-15s %-15s (%s)\n",
+ label,
+ inet_fmt(a->al_addr, s1),
+ inet_fmt(a->al_reporter, s2),
+ scaletime(timer_leftTimer(a->al_timerid)));
+ label = "";
+ }
+ label = "boundaries:";
+ for (acl = v->uv_acl; acl != NULL; acl = acl->acl_next) {
+ fprintf(fp, " %11s %-18s\n", label,
+ inet_fmts(acl->acl_addr, acl->acl_mask, s1));
+ label = "";
+ }
+ if (v->uv_filter) {
+ struct vf_element *vfe;
+ char lbuf[100];
+
+ sprintf(lbuf, "%5s %7s filter:",
+ v->uv_filter->vf_flags & VFF_BIDIR ? "bidir"
+ : " ",
+ v->uv_filter->vf_type == VFT_ACCEPT ? "accept"
+ : "deny");
+ label = lbuf;
+ for (vfe = v->uv_filter->vf_filter;
+ vfe != NULL; vfe = vfe->vfe_next) {
+ fprintf(fp, " %23s %-18s%s\n",
+ label,
+ inet_fmts(vfe->vfe_addr, vfe->vfe_mask, s1),
+ vfe->vfe_flags & VFEF_EXACT ? " (exact)" : "");
+ label = "";
+ }
+ }
+ if (!(v->uv_flags & (VIFF_TUNNEL|VIFF_DOWN|VIFF_DISABLED))) {
+ fprintf(fp, " IGMP querier: ");
+ if (v->uv_querier == NULL)
+ if (v->uv_flags & VIFF_QUERIER)
+ fprintf(fp, "%-18s (this system)\n",
+ inet_fmt(v->uv_lcl_addr, s1));
+ else
+ fprintf(fp, "NONE - querier election failure?\n");
+ else
+ fprintf(fp, "%-18s up %s last heard %s ago\n",
+ inet_fmt(v->uv_querier->al_addr, s1),
+ scaletime(now - v->uv_querier->al_ctime),
+ scaletime(v->uv_querier->al_timer));
+ }
+ if (v->uv_flags & VIFF_BLASTER)
+ fprintf(fp, " blasterbuf size: %dk\n",
+ v->uv_blasterlen / 1024);
+ fprintf(fp, " Nbr bitmaps: 0x%08lx%08lx\n",/*XXX*/
+ v->uv_nbrmap.hi, v->uv_nbrmap.lo);
+ if (v->uv_prune_lifetime != 0)
+ fprintf(fp, " Prune Lifetime: %d seconds\n",
+ v->uv_prune_lifetime);
+
+ v_req.vifi = vifi;
+ if (did_final_init)
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) {
+ log(LOG_WARNING, errno,
+ "SIOCGETVIFCNT fails on vif %d", vifi);
+ } else {
+ fprintf(fp, " pkts/bytes in : %lu/%lu\n",
+ v_req.icount, v_req.ibytes);
+ fprintf(fp, " pkts/bytes out: %lu/%lu\n",
+ v_req.ocount, v_req.obytes);
+ }
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "\n");
+}
+
+/*
+ * Time out record of a group membership on a vif
+ */
+static void
+DelVif(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *)arg;
+ vifi_t vifi = cbk->vifi;
+ struct uvif *v = &uvifs[vifi];
+ struct listaddr *a, **anp, *g = cbk->g;
+
+ /*
+ * Group has expired
+ * delete all kernel cache entries with this group
+ */
+ if (g->al_query)
+ DeleteTimer(g->al_query);
+
+ delete_lclgrp(vifi, g->al_addr);
+
+ anp = &(v->uv_groups);
+ while ((a = *anp) != NULL) {
+ if (a == g) {
+ *anp = a->al_next;
+ free((char *)a);
+ } else {
+ anp = &a->al_next;
+ }
+ }
+
+ free(cbk);
+}
+
+/*
+ * Set a timer to delete the record of a group membership on a vif.
+ */
+static int
+SetTimer(vifi, g)
+ vifi_t vifi;
+ struct listaddr *g;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->g = g;
+ cbk->vifi = vifi;
+ return timer_setTimer(g->al_timer, DelVif, cbk);
+}
+
+/*
+ * Delete a timer that was set above.
+ */
+static int
+DeleteTimer(id)
+ int id;
+{
+ timer_clearTimer(id);
+ return 0;
+}
+
+/*
+ * Send a group-specific query.
+ */
+static void
+SendQuery(arg)
+ void *arg;
+{
+ cbk_t *cbk = (cbk_t *)arg;
+ register struct uvif *v = &uvifs[cbk->vifi];
+
+ send_igmp(v->uv_lcl_addr, cbk->g->al_addr,
+ IGMP_MEMBERSHIP_QUERY,
+ cbk->q_time, cbk->g->al_addr, 0);
+ cbk->g->al_query = 0;
+ free(cbk);
+}
+
+/*
+ * Set a timer to send a group-specific query.
+ */
+static int
+SetQueryTimer(g, vifi, to_expire, q_time)
+ struct listaddr *g;
+ vifi_t vifi;
+ int to_expire, q_time;
+{
+ cbk_t *cbk;
+
+ cbk = (cbk_t *) malloc(sizeof(cbk_t));
+ cbk->g = g;
+ cbk->q_time = q_time;
+ cbk->vifi = vifi;
+ return timer_setTimer(to_expire, SendQuery, cbk);
+}
diff --git a/usr.sbin/mrouted/vif.h b/usr.sbin/mrouted/vif.h
new file mode 100644
index 0000000..5be618d
--- /dev/null
+++ b/usr.sbin/mrouted/vif.h
@@ -0,0 +1,237 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $FreeBSD$
+ * vif.h,v 3.8.4.26 1998/01/14 21:21:19 fenner Exp
+ */
+
+
+/*
+ * Bitmap handling functions.
+ * These should be fast but generic. bytes can be slow to zero and compare,
+ * words are hard to make generic. Thus two sets of macros (yuk).
+ */
+
+/*
+ * The VIFM_ functions should migrate out of <netinet/ip_mroute.h>, since
+ * the kernel no longer uses vifbitmaps.
+ */
+#ifndef VIFM_SET
+typedef u_long vifbitmap_t;
+
+#define VIFM_SET(n, m) ((m) |= (1 << (n)))
+#define VIFM_CLR(n, m) ((m) &= ~(1 << (n)))
+#define VIFM_ISSET(n, m) ((m) & (1 << (n)))
+#define VIFM_CLRALL(m) ((m) = 0x00000000)
+#define VIFM_COPY(mfrom, mto) ((mto) = (mfrom))
+#define VIFM_SAME(m1, m2) ((m1) == (m2))
+#endif
+/*
+ * And <netinet/ip_mroute.h> was missing some required functions anyway
+ */
+#ifndef VIFM_SETALL
+#define VIFM_SETALL(m) ((m) = ~0)
+#endif
+#define VIFM_ISSET_ONLY(n, m) ((m) == (1 << (n)))
+#define VIFM_ISEMPTY(m) ((m) == 0)
+#define VIFM_CLR_MASK(m, mask) ((m) &= ~(mask))
+#define VIFM_SET_MASK(m, mask) ((m) |= (mask))
+
+/*
+ * Neighbor bitmaps are, for efficiency, implemented as a struct
+ * containing two variables of a native machine type. If you
+ * have a native type that's bigger than a long, define it below.
+ */
+#define NBRTYPE u_long
+#define NBRBITS sizeof(NBRTYPE) * 8
+
+typedef struct {
+ NBRTYPE hi;
+ NBRTYPE lo;
+} nbrbitmap_t;
+#define MAXNBRS 2 * NBRBITS
+#define NO_NBR MAXNBRS
+
+#define NBRM_SET(n, m) (((n) < NBRBITS) ? ((m).lo |= (1 << (n))) : \
+ ((m).hi |= (1 << (n - NBRBITS))))
+#define NBRM_CLR(n, m) (((n) < NBRBITS) ? ((m).lo &= ~(1 << (n))) : \
+ ((m).hi &= ~(1 << (n - NBRBITS))))
+#define NBRM_ISSET(n, m) (((n) < NBRBITS) ? ((m).lo & (1 << (n))) : \
+ ((m).hi & (1 << ((n) - NBRBITS))))
+#define NBRM_CLRALL(m) ((m).lo = (m).hi = 0)
+#define NBRM_COPY(mfrom, mto) ((mto).lo = (mfrom).lo, (mto).hi = (mfrom).hi)
+#define NBRM_SAME(m1, m2) (((m1).lo == (m2).lo) && ((m1).hi == (m2).hi))
+#define NBRM_ISEMPTY(m) (((m).lo == 0) && ((m).hi == 0))
+#define NBRM_SETMASK(m, mask) (((m).lo |= (mask).lo),((m).hi |= (mask).hi))
+#define NBRM_CLRMASK(m, mask) (((m).lo &= ~(mask).lo),((m).hi &= ~(mask).hi))
+#define NBRM_MASK(m, mask) (((m).lo &= (mask).lo),((m).hi &= (mask).hi))
+#define NBRM_ISSETMASK(m, mask) (((m).lo & (mask).lo) || ((m).hi & (mask).hi))
+#define NBRM_ISSETALLMASK(m, mask)\
+ ((((m).lo & (mask).lo) == (mask).lo) && \
+ (((m).hi & (mask).hi) == (mask).hi))
+/*
+ * This macro is TRUE if all the subordinates have been pruned, or if
+ * there are no subordinates on this vif.
+ * The arguments is the map of subordinates, the map of neighbors on the
+ * vif, and the map of received prunes.
+ */
+#define SUBS_ARE_PRUNED(sub, vifmask, prunes) \
+ (((sub).lo & (vifmask).lo) == ((prunes).lo & (vifmask).lo & (sub).lo) && \
+ ((sub).hi & (vifmask).hi) == ((prunes).hi & (vifmask).hi & (sub).hi))
+
+struct blastinfo {
+ char * bi_buf; /* Pointer to malloced storage */
+ char * bi_cur; /* The update to process next */
+ char * bi_end; /* The place to put the next update */
+ int bi_len; /* Size of malloced storage */
+ int bi_timer; /* Timer to run process_blaster_report */
+};
+
+/*
+ * User level Virtual Interface structure
+ *
+ * A "virtual interface" is either a physical, multicast-capable interface
+ * (called a "phyint") or a virtual point-to-point link (called a "tunnel").
+ * (Note: all addresses, subnet numbers and masks are kept in NETWORK order.)
+ */
+struct uvif {
+ u_int uv_flags; /* VIFF_ flags defined below */
+ u_char uv_metric; /* cost of this vif */
+ u_char uv_admetric; /* advertised cost of this vif */
+ u_char uv_threshold; /* min ttl required to forward on vif */
+ u_int uv_rate_limit; /* rate limit on this vif */
+ u_int32 uv_lcl_addr; /* local address of this vif */
+ u_int32 uv_rmt_addr; /* remote end-point addr (tunnels only) */
+ u_int32 uv_dst_addr; /* destination for DVMRP messages */
+ u_int32 uv_subnet; /* subnet number (phyints only) */
+ u_int32 uv_subnetmask; /* subnet mask (phyints only) */
+ u_int32 uv_subnetbcast;/* subnet broadcast addr (phyints only) */
+ char uv_name[IFNAMSIZ]; /* interface name */
+ struct listaddr *uv_groups; /* list of local groups (phyints only) */
+ struct listaddr *uv_neighbors; /* list of neighboring routers */
+ nbrbitmap_t uv_nbrmap; /* bitmap of active neighboring routers */
+ struct listaddr *uv_querier; /* IGMP querier on vif */
+ int uv_igmpv1_warn;/* To rate-limit IGMPv1 warnings */
+ int uv_prune_lifetime; /* Prune lifetime or 0 for default */
+ struct vif_acl *uv_acl; /* access control list of groups */
+ int uv_leaf_timer; /* time until this vif is considrd leaf */
+ struct phaddr *uv_addrs; /* Additional subnets on this vif */
+ struct vif_filter *uv_filter; /* Route filters on this vif */
+ struct blastinfo uv_blaster; /* Info about route blasters */
+ int uv_nbrup; /* Counter for neighbor up events */
+ int uv_icmp_warn; /* To rate-limit ICMP warnings */
+ u_int uv_nroutes; /* # of routes with this vif as parent */
+ struct ip *uv_encap_hdr; /* Pre-formed header to encapsulate msgs*/
+};
+
+#define uv_blasterbuf uv_blaster.bi_buf
+#define uv_blastercur uv_blaster.bi_cur
+#define uv_blasterend uv_blaster.bi_end
+#define uv_blasterlen uv_blaster.bi_len
+#define uv_blastertimer uv_blaster.bi_timer
+
+#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL|VIFF_SRCRT)
+#define VIFF_DOWN 0x000100 /* kernel state of interface */
+#define VIFF_DISABLED 0x000200 /* administratively disabled */
+#define VIFF_QUERIER 0x000400 /* I am the subnet's querier */
+#define VIFF_ONEWAY 0x000800 /* Maybe one way interface */
+#define VIFF_LEAF 0x001000 /* all neighbors are leaves */
+#define VIFF_IGMPV1 0x002000 /* Act as an IGMPv1 Router */
+#define VIFF_REXMIT_PRUNES 0x004000 /* retransmit prunes */
+#define VIFF_PASSIVE 0x008000 /* passive tunnel */
+#define VIFF_ALLOW_NONPRUNERS 0x010000 /* ok to peer with nonprunrs */
+#define VIFF_NOFLOOD 0x020000 /* don't flood on this vif */
+#define VIFF_NOTRANSIT 0x040000 /* don't transit these vifs */
+#define VIFF_BLASTER 0x080000 /* nbr on vif blasts routes */
+#define VIFF_FORCE_LEAF 0x100000 /* ignore nbrs on this vif */
+#define VIFF_OTUNNEL 0x200000 /* DVMRP msgs "beside" tunnel*/
+
+#define AVOID_TRANSIT(v, r) \
+ (((r)->rt_parent != NO_VIF) && \
+ ((r)->rt_gateway != 0) && \
+ (uvifs[(v)].uv_flags & VIFF_NOTRANSIT) && \
+ (uvifs[(r)->rt_parent].uv_flags & VIFF_NOTRANSIT))
+
+struct phaddr {
+ struct phaddr *pa_next;
+ u_int32 pa_subnet; /* extra subnet */
+ u_int32 pa_subnetmask; /* netmask of extra subnet */
+ u_int32 pa_subnetbcast; /* broadcast of extra subnet */
+};
+
+struct vif_acl {
+ struct vif_acl *acl_next; /* next acl member */
+ u_int32 acl_addr; /* Group address */
+ u_int32 acl_mask; /* Group addr. mask */
+};
+
+struct vif_filter {
+ int vf_type;
+#define VFT_ACCEPT 1
+#define VFT_DENY 2
+ int vf_flags;
+#define VFF_BIDIR 1
+ struct vf_element *vf_filter;
+};
+
+struct vf_element {
+ struct vf_element *vfe_next;
+ u_int32 vfe_addr;
+ u_int32 vfe_mask;
+ int vfe_flags;
+#define VFEF_EXACT 0x0001
+};
+
+struct listaddr {
+ struct listaddr *al_next; /* link to next addr, MUST BE FIRST */
+ u_int32 al_addr; /* local group or neighbor address */
+ u_long al_timer; /* for timing out group or neighbor */
+ time_t al_ctime; /* entry creation time */
+ union {
+ struct {
+ u_int32 alur_genid; /* generation id for neighbor */
+ u_int alur_nroutes; /* # of routes w/ nbr as parent */
+ u_char alur_pv; /* router protocol version */
+ u_char alur_mv; /* router mrouted version */
+ u_char alur_index; /* neighbor index */
+ } alu_router;
+ struct {
+ u_int32 alug_reporter; /* a host which reported membership */
+ u_long alug_timerid; /* timer for group membership */
+ u_long alug_query; /* timer for repeated leave query */
+ u_char alug_old; /* time since heard old report */
+ } alu_group;
+ } al_alu;
+ u_short al_flags; /* flags related to this neighbor */
+};
+#define al_genid al_alu.alu_router.alur_genid
+#define al_nroutes al_alu.alu_router.alur_nroutes
+#define al_pv al_alu.alu_router.alur_pv
+#define al_mv al_alu.alu_router.alur_mv
+#define al_index al_alu.alu_router.alur_index
+#define al_reporter al_alu.alu_group.alug_reporter
+#define al_old al_alu.alu_group.alug_old
+#define al_timerid al_alu.alu_group.alug_timerid
+#define al_query al_alu.alu_group.alug_query
+
+#define NBRF_LEAF 0x0001 /* This neighbor is a leaf */
+#define NBRF_GENID 0x0100 /* I know this neighbor's genid */
+#define NBRF_WAITING 0x0200 /* Waiting for peering to come up */
+#define NBRF_ONEWAY 0x0400 /* One-way peering */
+#define NBRF_TOOOLD 0x0800 /* Too old (policy decision) */
+#define NBRF_TOOMANYROUTES 0x1000 /* Neighbor is spouting routes */
+#define NBRF_NOTPRUNING 0x2000 /* Neighbor doesn't appear to prune */
+
+/*
+ * Don't peer with neighbors with any of these flags set
+ */
+#define NBRF_DONTPEER (NBRF_WAITING|NBRF_ONEWAY|NBRF_TOOOLD| \
+ NBRF_TOOMANYROUTES|NBRF_NOTPRUNING)
+
+#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */
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..5976dd1
--- /dev/null
+++ b/usr.sbin/mtest/mtest.8
@@ -0,0 +1,57 @@
+.\"
+.\" $FreeBSD$
+.\"
+.\" The following requests are required for all man pages.
+.Dd December 15, 1996
+.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" -compact -offset indent
+.It Ic j Ar g.g.g.g Ar i.i.i.i
+Join the IP group address
+.Ar g.g.g.g
+on the interface with address
+.Ar i.i.i.i .
+.Ar i.i.i.i
+may be specified as 0.0.0.0 to use the default interface.
+.It Ic l Ar g.g.g.g Ar i.i.i.i
+Leave the IP group address
+.Ar g.g.g.g
+on the interface with address
+.Ar i.i.i.i .
+.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 ?\&
+List legal commands.
+.It Ic q
+Quit the program.
+.El
+.\" .Sh SEE ALSO
+.Sh AUTHORS
+.An Steve Deering
+.Sh BUGS
+The command parser is not very flexible.
diff --git a/usr.sbin/mtest/mtest.c b/usr.sbin/mtest/mtest.c
new file mode 100644
index 0000000..f13bf49
--- /dev/null
+++ b/usr.sbin/mtest/mtest.c
@@ -0,0 +1,228 @@
+/*
+ * Program to test new [sg]etsockopts and ioctls for manipulating IP and
+ * Ethernet multicast address filters.
+ *
+ * Written by Steve Deering, Stanford University, February 1989.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+
+int
+main( argc, argv )
+ int argc;
+ char **argv;
+ {
+ int so;
+ char line[80];
+ char *lineptr;
+ struct ip_mreq imr;
+ struct ifreq ifr;
+ int n, f;
+ unsigned i1, i2, i3, i4, g1, g2, g3, g4;
+ unsigned e1, e2, e3, e4, e5, e6;
+
+ if( (so = socket( AF_INET, SOCK_DGRAM, 0 )) == -1)
+ err( 1, "can't open socket" );
+
+ printf( "multicast membership test program; " );
+ printf( "enter ? for list of commands\n" );
+
+ while( fgets( line, 79, stdin ) != NULL )
+ {
+ lineptr = line;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ switch( *lineptr )
+ {
+ case '?':
+ {
+ printf( "%s%s%s%s%s%s%s",
+ " j g.g.g.g i.i.i.i - join IP multicast group \n",
+ " l g.g.g.g i.i.i.i - leave IP multicast group \n",
+ " a ifname e.e.e.e.e.e - add ether multicast address \n",
+ " d ifname e.e.e.e.e.e - del ether multicast address \n",
+ " m ifname 1/0 - set/clear ether allmulti flag \n",
+ " p ifname 1/0 - set/clear ether promisc flag \n",
+ " q - quit \n\n" );
+ break;
+ }
+
+ case 'j':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%u.%u.%u.%u %u.%u.%u.%u",
+ &g1, &g2, &g3, &g4, &i1, &i2, &i3, &i4 )) != 8 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ imr.imr_multiaddr.s_addr = (g1<<24) | (g2<<16) | (g3<<8) | g4;
+ imr.imr_multiaddr.s_addr = htonl(imr.imr_multiaddr.s_addr);
+ imr.imr_interface.s_addr = (i1<<24) | (i2<<16) | (i3<<8) | i4;
+ imr.imr_interface.s_addr = htonl(imr.imr_interface.s_addr);
+ if( setsockopt( so, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq) ) == -1 )
+ warn( "can't join group" );
+ else printf( "group joined\n" );
+ break;
+ }
+
+ case 'l':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%u.%u.%u.%u %u.%u.%u.%u",
+ &g1, &g2, &g3, &g4, &i1, &i2, &i3, &i4 )) != 8 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ imr.imr_multiaddr.s_addr = (g1<<24) | (g2<<16) | (g3<<8) | g4;
+ imr.imr_multiaddr.s_addr = htonl(imr.imr_multiaddr.s_addr);
+ imr.imr_interface.s_addr = (i1<<24) | (i2<<16) | (i3<<8) | i4;
+ imr.imr_interface.s_addr = htonl(imr.imr_interface.s_addr);
+ if( setsockopt( so, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq) ) == -1 )
+ warn( "can't leave group" );
+ else printf( "group left\n" );
+ break;
+ }
+
+ case 'a':
+ {
+ struct sockaddr_dl *dlp;
+ unsigned char *bp;
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %x.%x.%x.%x.%x.%x",
+ ifr.ifr_name, &e1, &e2, &e3, &e4, &e5, &e6 )) != 7 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ 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 = 6;
+ dlp->sdl_slen = 0;
+ bp = LLADDR(dlp);
+ bp[0] = e1;
+ bp[1] = e2;
+ bp[2] = e3;
+ bp[3] = e4;
+ bp[4] = e5;
+ bp[5] = e6;
+ if( ioctl( so, SIOCADDMULTI, &ifr ) == -1 )
+ warn( "can't add ether address" );
+ else printf( "ether address added\n" );
+ break;
+ }
+
+ case 'd':
+ {
+ struct sockaddr_dl *dlp;
+ unsigned char *bp;
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %x.%x.%x.%x.%x.%x",
+ ifr.ifr_name, &e1, &e2, &e3, &e4, &e5, &e6 )) != 7 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ 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 = 6;
+ dlp->sdl_slen = 0;
+ bp = LLADDR(dlp);
+ bp[0] = e1;
+ bp[1] = e2;
+ bp[2] = e3;
+ bp[3] = e4;
+ bp[4] = e5;
+ bp[5] = e6;
+ if( ioctl( so, SIOCDELMULTI, &ifr ) == -1 )
+ warn( "can't delete ether address" );
+ else printf( "ether address deleted\n" );
+ break;
+ }
+
+ case 'm':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %u", ifr.ifr_name, &f )) != 2 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ if( ioctl( so, SIOCGIFFLAGS, &ifr ) == -1 )
+ {
+ warn( "can't get interface flags" );
+ break;
+ }
+ printf( "interface flags %x, ", ifr.ifr_flags );
+ fflush( stdout );
+ if( f ) ifr.ifr_flags |= IFF_ALLMULTI;
+ else ifr.ifr_flags &= ~IFF_ALLMULTI;
+ if( ioctl( so, SIOCSIFFLAGS, &ifr ) == -1 )
+ warn( "can't set" );
+ else printf( "changed to %x\n", ifr.ifr_flags );
+ break;
+ }
+
+ case 'p':
+ {
+ ++lineptr;
+ while( *lineptr == ' ' || *lineptr == '\t' ) ++lineptr;
+ if( (n = sscanf( lineptr, "%s %u", ifr.ifr_name, &f )) != 2 )
+ {
+ printf( "bad args\n" );
+ break;
+ }
+ if( ioctl( so, SIOCGIFFLAGS, &ifr ) == -1 )
+ {
+ warn( "can't get interface flags" );
+ break;
+ }
+ printf( "interface flags %x, ", ifr.ifr_flags );
+ fflush( stdout );
+ if( f ) ifr.ifr_flags |= IFF_PROMISC;
+ else ifr.ifr_flags &= ~IFF_PROMISC;
+ if( ioctl( so, SIOCSIFFLAGS, &ifr ) == -1 )
+ warn( "can't set" );
+ else printf( "changed to %x\n", ifr.ifr_flags );
+ break;
+ }
+
+ case 'q': exit( 0 );
+
+ case 0:
+ case '\n': break;
+
+ default:
+ {
+ printf( "bad command\n" );
+ break;
+ }
+ }
+ }
+ return(0);
+ }
diff --git a/usr.sbin/mtree/Makefile b/usr.sbin/mtree/Makefile
new file mode 100644
index 0000000..6376ea3
--- /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
+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
+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..bf4353c
--- /dev/null
+++ b/usr.sbin/mtree/compare.c
@@ -0,0 +1,367 @@
+/*-
+ * 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
+#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 */
+
+ 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..fa902e4
--- /dev/null
+++ b/usr.sbin/mtree/create.c
@@ -0,0 +1,415 @@
+/*-
+ * 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
+#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.%ld",
+ (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 */
+ 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..cc211f0
--- /dev/null
+++ b/usr.sbin/mtree/misc.c
@@ -0,0 +1,120 @@
+/*-
+ * 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},
+#ifdef RMD160
+ {"ripemd160digest", F_RMD160, NEEDVALUE},
+#endif
+#ifdef SHA1
+ {"sha1digest", F_SHA1, 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.8 b/usr.sbin/mtree/mtree.8
new file mode 100644
index 0000000..3b76170
--- /dev/null
+++ b/usr.sbin/mtree/mtree.8
@@ -0,0 +1,376 @@
+.\" 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. 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: @(#)mtree.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd January 11, 2004
+.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
+Don't 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
+Don't 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
+Don't 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 specfication.
+.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 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 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.
+.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 EXAMPLES
+To detect system binaries that have been ``trojan horsed'', it is recommended
+that
+.Nm
+.Fl K
+.Cm sha1digest
+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 md5 1
+utility.
+Then, periodically,
+.Nm
+and
+.Xr md5 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 MD5 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.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh DIAGNOSTICS
+.Ex -std
+.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 .
+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..76f623c2
--- /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) && !getwd(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..7d899c6
--- /dev/null
+++ b/usr.sbin/mtree/mtree.h
@@ -0,0 +1,95 @@
+/*-
+ * 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 *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 */
+ 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..0407f78
--- /dev/null
+++ b/usr.sbin/mtree/spec.c
@@ -0,0 +1,312 @@
+/*-
+ * 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_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_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 != '.')
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ val = ep + 1;
+ ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
+ 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..9a76964
--- /dev/null
+++ b/usr.sbin/mtree/specspec.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Please see src/share/examples/etc/bsd-style-copyright.
+ */
+
+#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_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 (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..e320c4f
--- /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,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/verify.c b/usr.sbin/mtree/verify.c
new file mode 100644
index 0000000..7f2cf8db
--- /dev/null
+++ b/usr.sbin/mtree/verify.c
@@ -0,0 +1,233 @@
+/*-
+ * 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
+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, NULL)) == 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;
+
+ for (; p; p = p->next) {
+ 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))
+ (void)printf("%s: user/group not modified: %s\n",
+ path, 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)) {
+ (void)printf("%s: user/group/mode not modified: %s\n",
+ path, strerror(errno));
+ (void)printf("%s: warning: file mode %snot set\n", path,
+ (p->flags & F_FLAGS) ? "and file flags " : "");
+ continue;
+ }
+ 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.reload/Makefile b/usr.sbin/named.reload/Makefile
new file mode 100644
index 0000000..5cb467a1
--- /dev/null
+++ b/usr.sbin/named.reload/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+SCRIPTS= named.reload
+MAN= named.reload.8
+CLEANFILES+= ${SCRIPTS}
+SCRIPTSNAME= ${SCRIPTS}
+
+named.reload: named.reload.sh ${BIND_DIR}/Makefile
+ sed -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ < ${.CURDIR}/named.reload.sh > ${.TARGET}
+
+.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..6bda6c9
--- /dev/null
+++ b/usr.sbin/named.reload/named.reload.8
@@ -0,0 +1,70 @@
+.\" ++Copyright++ 1987, 1993
+.\" -
+.\" Copyright (c) 1987, 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.
+.\" -
+.\" Portions Copyright (c) 1993 by Digital Equipment Corporation.
+.\"
+.\" 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, and that
+.\" the name of Digital Equipment Corporation not be used in advertising or
+.\" publicity pertaining to distribution of the document or software without
+.\" specific, written prior permission.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+.\" CORPORATION 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.
+.\" -
+.\" --Copyright--
+.\"
+.\" from hostname.7 6.4 (Berkeley) 1/16/90
+.\" $FreeBSD$
+.\"
+.Dd June 26, 1993
+.Dt @INDOT_U@NAMED.RELOAD @SYS_OPS_EXT_U@
+.Os
+.Sh NAME
+.Nm @INDOT@named.reload
+.Nd "cause the name server to synchronize its database"
+.Sh DESCRIPTION
+This command runs
+.Xr ndc @SYS_OPS_EXT@
+which reloads the running name server.
+.Sh SEE ALSO
+.Xr @INDOT@named @SYS_OPS_EXT@ ,
+.Xr @INDOT@named.restart @SYS_OPS_EXT@ ,
+.Xr @INDOT@ndc @SYS_OPS_EXT@
diff --git a/usr.sbin/named.reload/named.reload.sh b/usr.sbin/named.reload/named.reload.sh
new file mode 100644
index 0000000..4a657a7
--- /dev/null
+++ b/usr.sbin/named.reload/named.reload.sh
@@ -0,0 +1,7 @@
+#!/bin/sh -
+#
+# from named.reload 5.2 (Berkeley) 6/27/89
+# $FreeBSD$
+#
+
+exec %DESTSBIN%/%INDOT%ndc reload
diff --git a/usr.sbin/named.restart/Makefile b/usr.sbin/named.restart/Makefile
new file mode 100644
index 0000000..e974bf8
--- /dev/null
+++ b/usr.sbin/named.restart/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+SCRIPTS= named.restart
+MAN= named.restart.8
+CLEANFILES+= ${SCRIPTS}
+SCRIPTSNAME= ${SCRIPTS}
+
+named.restart: named.restart.sh ${BIND_DIR}/Makefile
+ sed -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ < ${.CURDIR}/named.restart.sh > named.restart
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named.restart/named.restart.8 b/usr.sbin/named.restart/named.restart.8
new file mode 100644
index 0000000..2270225
--- /dev/null
+++ b/usr.sbin/named.restart/named.restart.8
@@ -0,0 +1,77 @@
+.\" ++Copyright++ 1987, 1993
+.\" -
+.\" Copyright (c) 1987, 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.
+.\" -
+.\" Portions Copyright (c) 1993 by Digital Equipment Corporation.
+.\"
+.\" 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, and that
+.\" the name of Digital Equipment Corporation not be used in advertising or
+.\" publicity pertaining to distribution of the document or software without
+.\" specific, written prior permission.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+.\" WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+.\" CORPORATION 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.
+.\" -
+.\" --Copyright--
+.\"
+.\" from hostname.7 6.4 (Berkeley) 1/16/90
+.\" $FreeBSD$
+.\"
+.Dd June 26, 1993
+.Dt @INDOT_U@NAMED.RESTART @SYS_OPS_EXT_U@
+.Os
+.Sh NAME
+.Nm @INDOT@named.restart
+.Nd "stop and restart the name server"
+.Sh DESCRIPTION
+This command runs
+.Xr ndc @SYS_OPS_EXT@
+which restarts the running name server with the command line options
+specified in
+.Xr rc.conf 5 .
+.Sh BUGS
+Does not wait after killing the old server before starting a new one; since
+the server could take some time to die and the new one will experience a
+fatal error if the old one isn't gone by the time it starts, you can be left
+in a situation where you have no name server at all.
+.Sh SEE ALSO
+.Xr @INDOT@named @SYS_OPS_EXT@ ,
+.Xr @INDOT@named.reload @SYS_OPS_EXT@ ,
+.Xr @INDOT@ndc @SYS_OPS_EXT@
diff --git a/usr.sbin/named.restart/named.restart.sh b/usr.sbin/named.restart/named.restart.sh
new file mode 100644
index 0000000..1fa6cf8
--- /dev/null
+++ b/usr.sbin/named.restart/named.restart.sh
@@ -0,0 +1,13 @@
+#!/bin/sh -
+#
+# from named.restart 5.4 (Berkeley) 6/27/89
+# $FreeBSD$
+#
+
+if [ -r /etc/defaults/rc.conf ]; then
+ . /etc/defaults/rc.conf
+ source_rc_confs
+elif [ -r /etc/rc.conf ]; then
+ . /etc/rc.conf
+fi
+exec %DESTSBIN%/%INDOT%ndc -n ${named_program} restart ${named_flags}
diff --git a/usr.sbin/named/Makefile b/usr.sbin/named/Makefile
new file mode 100644
index 0000000..4913c16
--- /dev/null
+++ b/usr.sbin/named/Makefile
@@ -0,0 +1,44 @@
+# $FreeBSD$
+
+USE_LIBBIND= true
+.include "${.CURDIR}/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/named ${BIND_DIR}/doc/man
+
+PROG= named
+MAN= named.conf.5 named.8 named-bootconf.8 nsupdate.8
+SRCS= tmp_version.c pathnames.h \
+ db_dump.c db_load.c db_lookup.c db_save.c db_update.c \
+ db_glue.c db_ixfr.c db_sec.c db_tsig.c \
+ ns_parser.y ns_lexer.c ns_parseutil.c ns_ctl.c \
+ ns_forw.c ns_init.c ns_main.c ns_maint.c ns_req.c \
+ ns_resp.c ns_stats.c ns_ncache.c ns_xfr.c ns_glue.c \
+ ns_udp.c ns_config.c ns_update.c ns_ixfr.c ns_signal.c \
+ ns_sort.c ns_notify.c
+
+.if exists(${.OBJDIR}/../../lib/libisc)
+LIBISCDIR:= ${.OBJDIR}/../../lib/libisc
+.else
+LIBISCDIR!= cd ${.CURDIR}/../../lib/libisc; make -V .OBJDIR
+.endif
+LIBISC:= ${LIBISCDIR}/libisc.a
+
+DPADD+= ${LIBISC}
+LDADD+= ${LIBISC}
+
+HTMLS= acl.html address_list.html comments.html config.html controls.html \
+ docdef.html example.html include.html index.html key.html \
+ logging.html master.html options.html server.html trusted-keys.html \
+ zone.html
+MISCS= DynamicUpdate FAQ.1of2 FAQ.2of2 rfc2317-notes.txt style.txt
+FILES= ${HTMLS} ${MISCS}
+.PATH: ${BIND_DIR}/doc/html ${BIND_DIR}/doc/misc
+
+.for file in ${HTMLS}
+FILESDIR_${file}= ${DOCDIR}/bind/html
+.endfor
+.for file in ${MISCS}
+FILESDIR_${file}= ${DOCDIR}/bind/misc
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named/Makefile.inc b/usr.sbin/named/Makefile.inc
new file mode 100644
index 0000000..30e1d2a
--- /dev/null
+++ b/usr.sbin/named/Makefile.inc
@@ -0,0 +1,59 @@
+# From: Id: Makefile.inc,v 8.4 1996/03/03 17:42:43 vixie Exp
+# $FreeBSD$
+
+.ifndef (Mk.Inc)
+Mk.Inc?=defined
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind
+
+VER!= cat ${BIND_DIR}/Version
+
+PS= ps
+PIDDIR= /var/run
+DESTETC= /etc/namedb
+DESTEXEC= /usr/libexec
+DESTRUN= /var/run
+DESTSBIN= /usr/sbin
+DESTHELP= /usr/share/misc
+
+CFLAGS+= -I${BIND_DIR}/port/freebsd/include \
+ -I${.CURDIR}/../../contrib/bind/bin/named
+
+# This is mostly for named and named-xfer
+.if defined(USE_LIBBIND)
+# Sadly, mkdep doesn't know about -idirafter, which would be ideal here.
+#CFLAGS+= -I${.CURDIR}/../../include -I${BIND_DIR}/include
+CFLAGS+= -I${BIND_DIR}/include
+
+.if exists(${.OBJDIR}/../../lib/libbind)
+LIBBINDDIR:= ${.OBJDIR}/../../lib/libbind
+.else
+LIBBINDDIR!= cd ${.CURDIR}/../../lib/libbind; make -V .OBJDIR
+.endif
+LIBBIND:= ${LIBBINDDIR}/libbind.a
+
+DPADD+= ${LIBBIND}
+LDADD+= ${LIBBIND}
+
+CLEANFILES+= tmp_version.c pathnames.h
+CFLAGS+= -I.
+
+tmp_version.c: version.c ${BIND_DIR}/Version
+ (u=$${USER-root} d=`pwd` h=`hostname` t=`LC_ALL=C date`; \
+ sed -e "s|%WHEN%|$${t}|" -e "s|%VERSION%|"${VER}"|" \
+ -e "s|%WHOANDWHERE%|$${u}@$${h}:$${d}|" \
+ < ${BIND_DIR}/bin/named/version.c > tmp_version.c)
+
+pathnames.h: ${BIND_DIR}/bin/named/pathtemplate.h \
+ ${.CURDIR}/../../usr.sbin/named/Makefile.inc
+ rm -f pathnames.h
+ sed -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ -e "s|%DESTEXEC%|${DESTEXEC}|" \
+ -e "s|%DESTETC%|${DESTETC}|" \
+ -e "s|%DESTRUN%|${DESTRUN}|" \
+ < ${BIND_DIR}/bin/named/pathtemplate.h > pathnames.h
+
+.endif
+
+.include "Makefile.maninc"
+.endif
diff --git a/usr.sbin/named/Makefile.maninc b/usr.sbin/named/Makefile.maninc
new file mode 100644
index 0000000..ee91be3
--- /dev/null
+++ b/usr.sbin/named/Makefile.maninc
@@ -0,0 +1,58 @@
+# From: Id: Makefile.maninc,v 8.1 1994/12/15 06:23:43 vixie Exp
+# $FreeBSD$
+
+# (the BIND kit's man page Makefile has gotten bad and crazy over the years.
+# this file has to be included AFTER bsd.prog.mk (which includes bsd.man.mk))
+CMD_EXT= 1
+SYSCALL_EXT= 2
+BSD_SYSCALL_EXT=2
+LIB_C_EXT= 3
+LIB_NETWORK_EXT=3
+FORMAT_EXT= 5
+DESC_EXT= 7
+SYS_OPS_EXT= 8
+
+EXT_SED_CMD = INDOT_U=`echo "${INDOT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export INDOT_U; \
+ XFER_INDOT_U=`echo "${XFER_INDOT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export XFER_INDOT_U; \
+ CMD_EXT_U=`echo "${CMD_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export CMD_EXT_U; \
+ SYS_OPS_EXT_U=`echo "${SYS_OPS_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export SYS_OPS_EXT_U; \
+ LIB_NETWORK_EXT_U=`echo "${LIB_NETWORK_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export LIB_NETWORK_EXT_U; \
+ FORMAT_EXT_U=`echo "${FORMAT_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export FORMAT_EXT_U; \
+ DESC_EXT_U=`echo "${DESC_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export DESC_EXT_U; \
+ SYSCALL_EXT_U=`echo "${SYSCALL_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export SYSCALL_EXT_U; \
+ BSD_SYSCALL_EXT_U=`echo "${BSD_SYSCALL_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export BSD_SYSCALL_EXT_U; \
+ LIB_C_EXT_U=`echo "${LIB_C_EXT}"|LC_ALL=C tr "a-z" "A-Z"`; \
+ export LIB_C_EXT_U; \
+ sed -e "s/@INDOT@/${INDOT}/g" \
+ -e "s/@INDOT_U@/$${INDOT_U}/g" \
+ -e "s/@XFER_INDOT@/${XFER_INDOT}/g" \
+ -e "s/@XFER_INDOT_U@/$${XFER_INDOT_U}/g" \
+ -e "s/@CMD_EXT@/${CMD_EXT}/g" \
+ -e "s/@CMD_EXT_U@/$${CMD_EXT_U}/g" \
+ -e "s/@LIB_NETWORK_EXT@/${LIB_NETWORK_EXT}/g" \
+ -e "s/@LIB_NETWORK_EXT_U@/$${LIB_NETWORK_EXT_U}/g" \
+ -e "s/@FORMAT_EXT@/${FORMAT_EXT}/g" \
+ -e "s/@FORMAT_EXT_U@/$${FORMAT_EXT_U}/g" \
+ -e "s/@DESC_EXT@/${DESC_EXT}/g" \
+ -e "s/@DESC_EXT_U@/$${DESC_EXT_U}/g" \
+ -e "s/@SYS_OPS_EXT@/${SYS_OPS_EXT}/g" \
+ -e "s/@SYS_OPS_EXT_U@/$${SYS_OPS_EXT_U}/g" \
+ -e "s/@SYSCALL_EXT@/${SYSCALL_EXT}/g" \
+ -e "s/@SYSCALL_EXT_U@/$${SYSCALL_EXT_U}/g" \
+ -e "s/@BSD_SYSCALL_EXT@/${BSD_SYSCALL_EXT}/g" \
+ -e "s/@BSD_SYSCALL_EXT_U@/$${BSD_SYSCALL_EXT_U}/g" \
+ -e "s/@LIB_C_EXT@/${LIB_C_EXT}/g" \
+ -e "s/@LIB_C_EXT_U@/$${LIB_C_EXT_U}/g" \
+ -e "s@/etc/named\.conf@/etc/namedb/named.conf@g" \
+ -e "s/^ *$$/.Pp/"
+
+MANFILTER= ${EXT_SED_CMD}
diff --git a/usr.sbin/ndc/Makefile b/usr.sbin/ndc/Makefile
new file mode 100644
index 0000000..daed12a
--- /dev/null
+++ b/usr.sbin/ndc/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+USE_LIBBIND= true
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/ndc ${BIND_DIR}/doc/man
+
+PROG= ndc
+MAN= ndc.8
+SRCS= pathnames.h \
+ ndc.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndiscvt/Makefile b/usr.sbin/ndiscvt/Makefile
new file mode 100644
index 0000000..c595818
--- /dev/null
+++ b/usr.sbin/ndiscvt/Makefile
@@ -0,0 +1,23 @@
+# $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
+
+MAN8= ndiscvt.8
+
+WARNS?= 4
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+YFLAGS+=-v
+
+CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/../../sys
+
+CLEANFILES= y.output
+
+.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..de6c342
--- /dev/null
+++ b/usr.sbin/ndiscvt/inf.c
@@ -0,0 +1,725 @@
+/*
+ * 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 section
+ *find_section (const char *);
+static void dump_deviceids_pci (void);
+static void dump_deviceids_pcmcia (void);
+static void dump_pci_id (const char *);
+static void dump_pcmcia_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();
+ 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 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_deviceids_pci()
+{
+ struct assign *manf, *dev;
+ struct section *sec;
+ struct assign *assign;
+ char xpsec[256];
+ int found = 0;
+
+ /* Find manufacturer name */
+ manf = find_assign("Manufacturer", NULL);
+
+ /* 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)) {
+ /* 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)
+ return;
+
+ found = 0;
+
+ /* Emit start of PCI device table */
+ fprintf (ofp, "#define NDIS_PCI_DEV_TABLE");
+
+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;
+ }
+
+ /* Emit end of table */
+
+ fprintf(ofp, "\n\n");
+
+}
+
+static void
+dump_deviceids_pcmcia()
+{
+ struct assign *manf, *dev;
+ struct section *sec;
+ struct assign *assign;
+ char xpsec[256];
+ int found = 0;
+
+ /* Find manufacturer name */
+ manf = find_assign("Manufacturer", NULL);
+
+ /* 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)) {
+ /* 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)
+ return;
+
+ found = 0;
+
+ /* Emit start of PCMCIA device table */
+ fprintf (ofp, "#define NDIS_PCMCIA_DEV_TABLE");
+
+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;
+ }
+
+ /* Emit end of table */
+
+ fprintf(ofp, "\n\n");
+
+}
+
+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);
+ break;
+ }
+ 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++;
+
+ /* Find manufacturer name */
+ manf = find_assign("Manufacturer", NULL);
+
+ /* 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)) {
+ 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]);
+
+ /* Emit start of block */
+ fprintf (ofp, "ndis_cfg ndis_regvals[] = {");
+
+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)
+ 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;
+ }
+
+ 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..6ba8a5b
--- /dev/null
+++ b/usr.sbin/ndiscvt/ndiscvt.8
@@ -0,0 +1,168 @@
+.\" 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
+.Fx
+.Sh SYNOPSIS
+.Nm
+.Op Fl i Ar inffile
+.Fl s Ar sysfile
+.Op Fl n Ar devname
+.Op Fl o Ar outfile
+.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
+indentifier 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.
+.El
+.Sh SEE ALSO
+.Xr ndis 4 ,
+.Xr ndisapi 9
+.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..d1a29b3
--- /dev/null
+++ b/usr.sbin/ndiscvt/ndiscvt.c
@@ -0,0 +1,290 @@
+/*
+ * 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 <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <err.h>
+
+#include <compat/ndis/pe_var.h>
+
+#include "inf.h"
+
+static int insert_padding(void **, int *);
+extern const char *__progname;
+
+/*
+ * Sections in object code files can be sparse. That is, the
+ * section may occupy more space in memory that it does when
+ * stored in a disk file. In Windows PE files, each section header
+ * has a 'virtual size' and 'raw data size' field. The latter
+ * specifies the amount of section data actually stored in the
+ * disk file, and the former describes how much space the section
+ * should actually occupy in memory. If the vsize is larger than
+ * the rsize, we need to allocate some extra storage and fill
+ * it with zeros. (Think BSS.)
+ *
+ * The typical method of loading an executable file involves
+ * reading each segment into memory using the vaddr/vsize from
+ * each section header. We try to make a small optimization however
+ * and only pad/move segments when it's absolutely necessary, i.e.
+ * if the vsize is larger than the rsize. This conserves a little
+ * bit of memory, at the cost of having to fixup some of the values
+ * in the section headers.
+ */
+
+#define ROUND_UP(x, y) \
+ (((x) + (y)) - ((x) % (y)))
+
+#define SET_HDRS(x) \
+ dos_hdr = (image_dos_header *)x; \
+ nt_hdr = (image_nt_header *)(x + dos_hdr->idh_lfanew); \
+ sect_hdr = (image_section_header *)((vm_offset_t)nt_hdr + \
+ sizeof(image_nt_header));
+
+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, diff, 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++) {
+ /*
+ * If we have accumulated any padding offset,
+ * add it to the raw data address of this segment.
+ */
+ oldraddr = sect_hdr->ish_rawdataaddr;
+ oldrlen = sect_hdr->ish_rawdatasize;
+ if (offaccum)
+ sect_hdr->ish_rawdataaddr += offaccum;
+ if (sect_hdr->ish_misc.ish_vsize >
+ sect_hdr->ish_rawdatasize) {
+ diff = ROUND_UP(sect_hdr->ish_misc.ish_vsize -
+ sect_hdr->ish_rawdatasize,
+ opt_hdr.ioh_filealign);
+ offaccum += ROUND_UP(diff -
+ (sect_hdr->ish_misc.ish_vsize -
+ sect_hdr->ish_rawdatasize),
+ opt_hdr.ioh_filealign);
+ sect_hdr->ish_rawdatasize =
+ 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 [-i <inffile>] -s <sysfile> "
+ "[-n devname] [-o outfile]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp, *outfp;
+ void *img;
+ int n, fsize, cnt;
+ unsigned char *ptr;
+ int i;
+ char *inffile = NULL, *sysfile = NULL, *outfile = NULL;
+ char *dname = NULL;
+ int ch;
+
+ while((ch = getopt(argc, argv, "i:s:o:n:")) != -1) {
+ switch(ch) {
+ case 'i':
+ inffile = optarg;
+ break;
+ case 's':
+ sysfile = optarg;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'n':
+ dname = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ 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");
+ fprintf(outfp, "\nextern unsigned char drv_data[];\n\n");
+
+ fprintf(outfp, "__asm__(\".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/ndp/Makefile b/usr.sbin/ndp/Makefile
new file mode 100644
index 0000000..c35dfc7
--- /dev/null
+++ b/usr.sbin/ndp/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$
+
+.PATH: ${.CURDIR}/../../contrib/tcpdump
+
+PROG= ndp
+MAN= ndp.8
+SRCS= ndp.c gmt2local.c
+
+CFLAGS+= -DINET6
+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..b39625a
--- /dev/null
+++ b/usr.sbin/ndp/ndp.8
@@ -0,0 +1,240 @@
+.\" $FreeBSD$
+.\" $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.
+.\"
+.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 flags ...
+.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 Ds
+.It Fl a
+Dump the currently existing NDP entries.
+The following information will be printed:
+.Bl -tag -width NeighborXX
+.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 flags ...
+View ND information for the specified interface.
+If additional arguments
+.Ar flags
+are given,
+.Nm
+sets or clears the specified flags for the interface.
+Each flag should be separated by white spaces or tab characters.
+Possible flags are as follows.
+All of the flags can begin with the
+special character
+.Ql - ,
+which means the flag should be cleared.
+Note that you need
+.Fl -
+before
+.Fl foo
+in this case.
+.\"
+.Pp
+.Bl -tag -width Ds -compact
+.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.
+.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 8 .
+Most useful when used with
+.Fl A .
+.El
+.\"
+.Sh DIAGNOSTICS
+.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..9f40ffa
--- /dev/null
+++ b/usr.sbin/ndp/ndp.c
@@ -0,0 +1,1582 @@
+/* $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.
+ * 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.
+ */
+
+/*
+ * 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 <err.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))
+
+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 __P((int, char **));
+int file __P((char *));
+void getsocket __P((void));
+int set __P((int, char **));
+void get __P((char *));
+int delete __P((char *));
+void dump __P((struct in6_addr *, int));
+static struct in6_nbrinfo *getnbrinfo __P((struct in6_addr *, int, int));
+static char *ether_str __P((struct sockaddr_dl *));
+int ndp_ether_aton __P((char *, u_char *));
+void usage __P((void));
+int rtmsg __P((int));
+void ifinfo __P((char *, int, char **));
+void rtrlist __P((void));
+void plist __P((void));
+void pfx_flush __P((void));
+void rtrlist __P((void));
+void rtr_flush __P((void));
+void harmonize_rtr __P((void));
+#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
+static void getdefif __P((void));
+static void setdefif __P((char *));
+#endif
+static char *sec2str __P((time_t));
+static char *ether_str __P((struct sockaddr_dl *));
+static void ts_print __P((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, 100, 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_LLINFO) &&
+ !(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;
+ 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_LLINFO) &&
+ !(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);
+ }
+ 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;
+ mib[5] = RTF_LLINFO;
+ 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);
+ if (rtm->rtm_flags & RTF_ANNOUNCE) {
+ rtm->rtm_flags &= ~RTF_HOST;
+ rtm->rtm_addrs |= RTA_NETMASK;
+ }
+ /* FALLTHROUGH */
+ case RTM_GET:
+ rtm->rtm_addrs |= RTA_DST;
+ }
+#define NEXTADDR(w, s) \
+ if (rtm->rtm_addrs & (w)) { \
+ bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
+
+ NEXTADDR(RTA_DST, sin_m);
+ NEXTADDR(RTA_GATEWAY, sdl_m);
+ memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ 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)
+ 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
+
+ ND.flags = newflags;
+ if (ioctl(s, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) {
+ err(1, "ioctl(SIOCSIFINFO_FLAGS)");
+ /* NOTREACHED */
+ }
+#undef SETFLAG
+ }
+
+ if (!ND.initialized) {
+ errx(1, "%s: not initialized yet", ifname);
+ /* 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;
+ }
+ for (j = 0; j < 8; j++)
+ printf("%02x", rbuf[j]);
+ }
+ }
+#endif
+ if (ND.flags) {
+ printf("\nFlags: ");
+ 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*/
+ }
+ 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);
+}
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..bbfef2d
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.8
@@ -0,0 +1,227 @@
+.\" 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 March 10, 2004
+.Dt NEWSYSLOG 8
+.Os
+.Sh NAME
+.Nm newsyslog
+.Nd maintain system log files to manageable sizes
+.Sh SYNOPSIS
+.Nm
+.Op Fl CFnrsv
+.Op Fl R Ar tagname
+.Op Fl a 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 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 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 BUGS
+Doesn't yet automatically read the logs to find security breaches.
+.Sh AUTHORS
+.An Theodore Ts'o ,
+MIT Project Athena
+.Pp
+Copyright 1987, Massachusetts Institute of Technology
+.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 "SEE ALSO"
+.Xr bzip 1 ,
+.Xr gzip 1 ,
+.Xr syslog 3 ,
+.Xr newsyslog.conf 5 ,
+.Xr chown 8 ,
+.Xr syslogd 8
diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c
new file mode 100644
index 0000000..c160432
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.c
@@ -0,0 +1,2367 @@
+/*-
+ * ------+---------+---------+-------- + --------+---------+---------+---------*
+ * 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"
+
+/* Define this symbol to try out the "new order" for work items. */
+#define TRY_NEWORDER
+#ifndef USE_NEWORDER
+#define USE_NEWORDER 1 /* Initial value for dbg_new_order */
+#endif
+
+/*
+ * Bit-values for the 'flags' parsed from a config-file entry.
+ */
+#define CE_COMPACT 0x0001 /* Compact the achived log files with gzip. */
+#define CE_BZCOMPACT 0x0002 /* Compact the achived log files with bzip2. */
+#define CE_COMPACTWAIT 0x0004 /* wait until compressing one file finishes */
+ /* before starting the next step. */
+#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 */
+ 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 */
+/*
+ * The debug options "neworder" and "oldorder" can be used to change
+ * which order work is done in. Note that both options will disappear
+ * in the near future, and the "new" order will be the only order.
+ */
+int dbg_new_order = USE_NEWORDER;
+
+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 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 */
+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 */
+
+char hostname[MAXHOSTNAMELEN]; /* hostname */
+char daytime[16]; /* The current time in human readable form,
+ * used for rotation-tracking messages. */
+
+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 *);
+#ifdef TRY_NEWORDER
+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 *);
+#endif
+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 void compress_log(char *logname, int dowait);
+static void bzcompress_log(char *logname, int dowait);
+static int age_old_log(char *file);
+static int send_signal(const struct conf_entry *ent);
+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;
+#ifdef TRY_NEWORDER
+ struct sigwork_entry *stmp;
+ struct zipwork_entry *ztmp;
+#endif
+
+ 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;
+ }
+
+#ifdef TRY_NEWORDER
+ /*
+ * 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);
+ }
+#endif /* TRY_NEWORDER */
+
+ 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);
+
+ tempwork->log = strdup(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)
+ strncpy(temp_reason, " (no -C option)", REASON_MAX);
+ else if (createlogs)
+ strncpy(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) {
+ 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
+}
+
+/* Send a signal to the pid specified by pidfile */
+static int
+send_signal(const struct conf_entry *ent)
+{
+ pid_t target_pid;
+ int did_notify;
+ FILE *f;
+ long minok, maxok, rval;
+ const char *target_name;
+ char *endp, *linep, line[BUFSIZ];
+
+ did_notify = 0;
+ f = fopen(ent->pid_file, "r");
+ if (f == NULL) {
+ warn("can't open pid file: %s", ent->pid_file);
+ return (did_notify);
+ /* NOTREACHED */
+ }
+
+ if (fgets(line, BUFSIZ, f) == NULL) {
+ /*
+ * XXX - If the pid file is empty, is that really a
+ * problem? Wouldn't that mean that the process
+ * has shut down? In that case there would be no
+ * problem with compressing the rotated log file.
+ */
+ if (feof(f))
+ 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 (did_notify);
+ /* NOTREACHED */
+ }
+ (void) fclose(f);
+
+ target_name = "daemon";
+ minok = MIN_PID;
+ maxok = MAX_PID;
+ 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.
+ */
+ target_name = "process-group";
+ minok = -MAX_PID;
+ maxok = -MIN_PID;
+ }
+
+ 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);
+ rval = 0;
+ } 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);
+ rval = 0;
+ }
+ if (rval == 0) {
+ return (did_notify);
+ /* NOTREACHED */
+ }
+
+ target_pid = rval;
+
+ if (noaction) {
+ did_notify = 1;
+ printf("\tkill -%d %d\n", ent->sig, (int) target_pid);
+ } else if (kill(target_pid, ent->sig)) {
+ /*
+ * XXX - Iff the error was "no such process", should that
+ * really be an error for us? Perhaps the process
+ * is already gone, in which case there would be no
+ * problem with compressing the rotated log file.
+ */
+ warn("can't notify %s, pid %d", target_name,
+ (int) target_pid);
+ } else {
+ did_notify = 1;
+ if (verbose)
+ printf("%s pid %d notified\n", target_name,
+ (int) target_pid);
+ }
+
+ return (did_notify);
+}
+
+static void
+parse_args(int argc, char **argv)
+{
+ int ch;
+ char *p;
+
+ timenow = ptime_init(NULL);
+ ptimeset_time(timenow, time(NULL));
+ (void)strncpy(daytime, ptimeget_ctime(timenow) + 4, 15);
+ daytime[15] = '\0';
+
+ /* 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:f:nrsvCD:FR:")) != -1)
+ switch (ch) {
+ case 'a':
+ archtodir++;
+ archdirname = 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 'R':
+ rotatereq++;
+ requestor = strdup(optarg);
+ break;
+ case 'm': /* Used by OpenBSD for "monitor mode" */
+ default:
+ 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 */
+ }
+
+ if (strcmp(doption, "neworder") == 0) {
+#ifdef TRY_NEWORDER
+ dbg_new_order++;
+#else
+ warnx("NOTE: The code for 'neworder' was not compiled in.");
+#endif
+ return (1); /* successfully parsed */
+ }
+ if (strcmp(doption, "oldorder") == 0) {
+#ifdef TRY_NEWORDER
+ dbg_new_order = 0;
+#else
+ warnx("NOTE: The code for 'neworder' was not compiled in.");
+#endif
+ return (1); /* successfully parsed */
+ }
+
+ warnx("Unknown -D (debug) option: '%s'", doption);
+ return (0); /* failure */
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: newsyslog [-CFnrsv] [-a 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", conf);
+
+ 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, 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':
+ working->flags |= CE_COMPACTWAIT;
+ 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, notified, need_notification, numlogs_c;
+ fk_entry free_or_keep;
+ 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);
+
+#ifdef TRY_NEWORDER
+ /*
+ * 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.
+ */
+ if (dbg_new_order) {
+ struct sigwork_entry *swork;
+
+ 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);
+ }
+#endif /* TRY_NEWORDER */
+
+ /*
+ * Find out if there is a process to signal. If nosignal (-s) was
+ * specified, then do not signal any process. Note that nosignal
+ * will trigger a warning message if the rotated logfile needs to
+ * be compressed, *unless* -R was specified. This is because there
+ * presumably still are process(es) writing to the old logfile, but
+ * we assume that a -sR request comes from a process which writes
+ * to the logfile, and as such, that process has already made sure
+ * that the logfile is not presently in use.
+ */
+ need_notification = notified = 0;
+ if (ent->pid_file != NULL) {
+ need_notification = 1;
+ if (!nosignal)
+ notified = send_signal(ent); /* the normal case! */
+ else if (rotatereq)
+ need_notification = 0;
+ }
+
+ if ((flags & CE_COMPACT) || (flags & CE_BZCOMPACT)) {
+ if (need_notification && !notified)
+ warnx(
+ "log %s.0 not compressed because daemon(s) not notified",
+ ent->log);
+ else if (noaction) {
+ printf("\tsleep 10\n");
+ if (flags & CE_COMPACT)
+ printf("\tgzip %s.0\n", ent->log);
+ else
+ printf("\tbzip2 %s.0\n", ent->log);
+ } else {
+ if (notified) {
+ if (verbose)
+ printf("small pause to allow daemon(s) to close log\n");
+ sleep(10);
+ }
+ if (archtodir) {
+ (void) snprintf(file1, sizeof(file1), "%s/%s",
+ dirpart, namepart);
+ if (flags & CE_COMPACT)
+ compress_log(file1,
+ flags & CE_COMPACTWAIT);
+ else if (flags & CE_BZCOMPACT)
+ bzcompress_log(file1,
+ flags & CE_COMPACTWAIT);
+ } else {
+ if (flags & CE_COMPACT)
+ compress_log(ent->log,
+ flags & CE_COMPACTWAIT);
+ else if (flags & CE_BZCOMPACT)
+ bzcompress_log(ent->log,
+ flags & CE_COMPACTWAIT);
+ }
+ }
+ }
+ return (free_or_keep);
+}
+
+#ifdef TRY_NEWORDER
+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 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;
+ }
+
+ 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) {
+ pgm_name = strrchr(pgm_path, '/');
+ if (pgm_name == NULL)
+ pgm_name = pgm_path;
+ else
+ pgm_name++;
+ printf("\t%s %s\n", pgm_name, zwork->zw_fname);
+ change_attrs(zresult, zwork->zw_conf);
+ return;
+ }
+
+ pidzip = fork();
+ if (pidzip < 0)
+ err(1, "gzip fork");
+ else if (!pidzip) {
+ /* The child process executes the compression command */
+ execl(pgm_path, pgm_path, "-f", zwork->zw_fname, (char *)0);
+ err(1, pgm_path);
+ }
+
+ wpid = waitpid(pidzip, &zstatus, 0);
+ if (wpid == -1) {
+ warn("%s: waitpid(%d)", pgm_path, pidzip);
+ return;
+ }
+ if (!WIFEXITED(zstatus)) {
+ warn("%s: did not terminate normally", pgm_path);
+ return;
+ }
+ if (WEXITSTATUS(zstatus)) {
+ warn("%s: terminated with %d (non-zero) status",
+ pgm_path, 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;
+}
+#endif /* TRY_NEWORDER */
+
+/* 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);
+}
+
+/*
+ * XXX - Note that both compress_log and bzcompress_log will lose the
+ * NODUMP flag if it was set on somelog.0. Fixing that in newsyslog
+ * (as opposed to fixing gzip/bzip2) will require some restructuring
+ * of the code. That restructuring is planned for a later update...
+ */
+/* Fork of gzip to compress the old log file */
+static void
+compress_log(char *logname, int dowait)
+{
+ pid_t pid;
+ char tmp[MAXPATHLEN];
+
+ while (dowait && (wait(NULL) > 0 || errno == EINTR))
+ ;
+ (void) snprintf(tmp, sizeof(tmp), "%s.0", logname);
+ pid = fork();
+ if (pid < 0)
+ err(1, "gzip fork");
+ else if (!pid) {
+ (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, (char *)0);
+ err(1, _PATH_GZIP);
+ }
+}
+
+/* Fork of bzip2 to compress the old log file */
+static void
+bzcompress_log(char *logname, int dowait)
+{
+ pid_t pid;
+ char tmp[MAXPATHLEN];
+
+ while (dowait && (wait(NULL) > 0 || errno == EINTR))
+ ;
+ snprintf(tmp, sizeof(tmp), "%s.0", logname);
+ pid = fork();
+ if (pid < 0)
+ err(1, "bzip2 fork");
+ else if (!pid) {
+ execl(_PATH_BZIP2, _PATH_BZIP2, "-f", tmp, (char *)0);
+ err(1, _PATH_BZIP2);
+ }
+}
+
+/* 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 = lstat(tempfile, &st);
+ if (failed && errno != ENOENT)
+ err(1, "Error on lstat(%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);
+}
+
+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)
+ warn("can'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..c6d69d8
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.conf.5
@@ -0,0 +1,362 @@
+.\" 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 June 3, 2004
+.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\e ;
+in this case preceding
+.Ql \e\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 effect 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 W
+if used with the
+.Cm Z
+or
+.Cm J
+flag, this indicates that
+.Xr newsyslog 8
+should wait for previously started compression jobs to complete before
+starting a new one for this entry.
+If this is used with the
+.Cm G
+flag and if multiple log files match the given pattern, then
+.Xr newsyslog 8
+will compress those logs one by one.
+This ensures that only one compression job is running at a time.
+.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 bzip 1 ,
+.Xr gzip 1 ,
+.Xr syslog 3 ,
+.Xr chown 8 ,
+.Xr newsyslog 8 ,
+.Xr syslog 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..5a7fe97
--- /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?= 0
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8
new file mode 100644
index 0000000..512257b
--- /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.
+.\" 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.
+.\"
+.\" @(#)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 DIAGNOSTICS
+.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..c8f3a42
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.c
@@ -0,0 +1,850 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 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 20
+#define DEFNFSDCNT 4
+pid_t children[MAXNFSDCNT]; /* PIDs of children */
+int nfsdcnt; /* number of children */
+
+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(argc, argv, envp)
+ int argc;
+ char *argv[], *envp[];
+{
+ struct nfsd_args nfsdargs;
+ 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, len, maxsock, msgsock;
+ 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);
+
+ /* 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);
+ }
+
+ (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);
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 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);
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 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");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (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");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inet6peer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (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(signo)
+ int signo;
+{
+ syslog(LOG_ERR, "missing system call: NFS not available");
+}
+
+void
+reapchild(signo)
+ 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(signo)
+{
+ nfsd_exit(0);
+}
+
+/*
+ * Cleanup child after SIGUSR1.
+ */
+void
+child_cleanup(signo)
+{
+ exit(0);
+}
+
+void
+nfsd_exit(int status)
+{
+ killchildren();
+ unregistration();
+ exit(status);
+}
+
+void
+start_server(int master)
+{
+ int status;
+
+ status = 0;
+ if (nfssvc(NFSSVC_NFSD, 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..364bbf7
--- /dev/null
+++ b/usr.sbin/ngctl/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# $Whistle: Makefile,v 1.3 1999/01/16 00:10:11 archie Exp $
+
+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
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ngctl/config.c b/usr.sbin/ngctl/config.c
new file mode 100644
index 0000000..9b4c2b7
--- /dev/null
+++ b/usr.sbin/ngctl/config.c
@@ -0,0 +1,104 @@
+/*
+ * 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 "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,
+ {}
+};
+
+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..460e6b2
--- /dev/null
+++ b/usr.sbin/ngctl/connect.c
@@ -0,0 +1,86 @@
+
+/*
+ * 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 "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..fbfce4e
--- /dev/null
+++ b/usr.sbin/ngctl/debug.c
@@ -0,0 +1,79 @@
+
+/*
+ * 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 "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.",
+ {}
+};
+
+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..d5a43f5
--- /dev/null
+++ b/usr.sbin/ngctl/dot.c
@@ -0,0 +1,195 @@
+
+/*
+ * 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 <inttypes.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, "")) != EOF) {
+ 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..e905d33
--- /dev/null
+++ b/usr.sbin/ngctl/list.c
@@ -0,0 +1,116 @@
+
+/*
+ * 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 "ngctl.h"
+
+static int ListCmd(int ac, char **av);
+
+const struct ngcmd list_cmd = {
+ ListCmd,
+ "list [-n]",
+ "Show information about all nodes",
+ "The list command shows information 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.",
+ { "ls" }
+};
+
+static int
+ListCmd(int ac, char **av)
+{
+ struct ng_mesg *resp;
+ struct namelist *nlist;
+ int named_only = 0;
+ int ch, rtn = CMDRTN_OK;
+ u_int k;
+
+ /* Get options */
+ optind = 1;
+ while ((ch = getopt(ac, av, "n")) != EOF) {
+ switch (ch) {
+ 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 " : "");
+ for (k = 0; k < nlist->numnames; k++) {
+ char path[NG_PATHSIZ];
+ char *argv[3] = { "list", "-n", path };
+
+ snprintf(path, sizeof(path),
+ "[%lx]:", (u_long) nlist->nodeinfo[k].id);
+ if ((rtn = (*show_cmd.func)(3, argv)) != CMDRTN_OK)
+ break;
+ }
+
+ /* 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..fcd61df
--- /dev/null
+++ b/usr.sbin/ngctl/main.c
@@ -0,0 +1,509 @@
+
+/*
+ * 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 "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 int DoParseCommand(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);
+
+/* 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:")) != EOF) {
+ 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);
+}
+
+/*
+ * Interactive mode
+ */
+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");
+ }
+
+ /* Display any incoming control message */
+ if (FD_ISSET(csock, &rfds))
+ MsgRead();
+
+ /* Display any incoming data packet */
+ if (FD_ISSET(dsock, &rfds)) {
+ u_char *buf;
+ char hook[NG_HOOKSIZ];
+ 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);
+ }
+
+ /* 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);
+}
+
+/*
+ * Parse a command line and execute the command
+ */
+static int
+DoParseCommand(char *line)
+{
+ char *av[MAX_ARGS];
+ int ac;
+
+ /* Parse line */
+ for (ac = 0, av[0] = strtok(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..621d318
--- /dev/null
+++ b/usr.sbin/ngctl/mkpeer.c
@@ -0,0 +1,86 @@
+
+/*
+ * 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 "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.",
+ {}
+};
+
+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..c71815b
--- /dev/null
+++ b/usr.sbin/ngctl/msg.c
@@ -0,0 +1,155 @@
+
+/*
+ * 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 "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(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..3de1360
--- /dev/null
+++ b/usr.sbin/ngctl/name.c
@@ -0,0 +1,76 @@
+
+/*
+ * 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 "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,
+ {}
+};
+
+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..b98d507
--- /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 DIAGNOSTICS
+.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..1f7826e
--- /dev/null
+++ b/usr.sbin/ngctl/ngctl.h
@@ -0,0 +1,102 @@
+
+/*
+ * 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$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+
+#include <netgraph.h>
+#include <netgraph/ng_socket.h>
+#include <netgraph/ng_message.h>
+
+#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..834cc0c
--- /dev/null
+++ b/usr.sbin/ngctl/rmhook.c
@@ -0,0 +1,82 @@
+
+/*
+ * 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 "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..6921ec4
--- /dev/null
+++ b/usr.sbin/ngctl/show.c
@@ -0,0 +1,133 @@
+
+/*
+ * 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 "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")) != EOF) {
+ 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..d391ba8
--- /dev/null
+++ b/usr.sbin/ngctl/shutdown.c
@@ -0,0 +1,75 @@
+
+/*
+ * 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 "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..8c10a85
--- /dev/null
+++ b/usr.sbin/ngctl/status.c
@@ -0,0 +1,96 @@
+
+/*
+ * 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 "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,
+ {}
+};
+
+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..30c5c16
--- /dev/null
+++ b/usr.sbin/ngctl/types.c
@@ -0,0 +1,95 @@
+
+/*
+ * 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 "ngctl.h"
+
+static int TypesCmd(int ac, char **av);
+
+const struct ngcmd types_cmd = {
+ TypesCmd,
+ "types",
+ "Show information about all installed node types",
+ 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..d1bdca0
--- /dev/null
+++ b/usr.sbin/ngctl/write.c
@@ -0,0 +1,111 @@
+
+/*
+ * 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 "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..213cf58
--- /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 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.
+.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
diff --git a/usr.sbin/nologin/Makefile b/usr.sbin/nologin/Makefile
new file mode 100644
index 0000000..48efba2
--- /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.
+NOSHARED= 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..10ffbb8
--- /dev/null
+++ b/usr.sbin/nologin/nologin.5
@@ -0,0 +1,68 @@
+.\" 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.
+.\"
+.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93
+.\" $FreeBSD$
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 5
+.Os
+.Sh NAME
+.Nm nologin
+.Nd disallow logins
+.Sh DESCRIPTION
+Programs such as
+.Xr login 1
+disallow logins if the file
+.Pa /var/run/nologin
+exists.
+Programs display the contents of
+.Pa /var/run/nologin
+to the user and exit.
+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
+Ignored by
+.Xr login 1
+for user root.
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/nologin" -compact
+.It Pa /var/run/nologin
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr rlogin 1 ,
+.Xr telnet 1 ,
+.Xr 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..7f8f9ff
--- /dev/null
+++ b/usr.sbin/nologin/nologin.8
@@ -0,0 +1,61 @@
+.\" 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.
+.\"
+.\" @(#)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..a13c517
--- /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(int argc, char *argv[])
+{
+ 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/nslookup/Makefile b/usr.sbin/nslookup/Makefile
new file mode 100644
index 0000000..788b18f
--- /dev/null
+++ b/usr.sbin/nslookup/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+USE_LIBBIND= yes
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/nslookup ${BIND_DIR}/doc/man
+
+PROG= nslookup
+MAN= nslookup.8
+SRCS= main.c getinfo.c debug.c send.c skip.c list.c subr.c commands.l
+
+CFLAGS+= -D_PATH_HELPFILE=\"${DESTHELP}/nslookup.help\"
+CFLAGS+= -I${.CURDIR}/../../contrib/bind/bin/nslookup
+
+DPADD+= ${LIBL} ${LIBEDIT} ${LIBTERMCAP}
+LDADD+= -ll -ledit -ltermcap
+
+FILES= nslookup.help
+FILESDIR= ${DESTHELP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nsupdate/Makefile b/usr.sbin/nsupdate/Makefile
new file mode 100644
index 0000000..16697cb
--- /dev/null
+++ b/usr.sbin/nsupdate/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+USE_LIBBIND= yes
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/nsupdate
+
+PROG= nsupdate
+NOMAN= hmm..
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/Makefile b/usr.sbin/ntp/Makefile
new file mode 100644
index 0000000..8f665da
--- /dev/null
+++ b/usr.sbin/ntp/Makefile
@@ -0,0 +1,11 @@
+# Makefile for ntpd.
+# $FreeBSD$
+
+SUBDIR= libntp libparse ntpd ntpdc ntpq ntpdate ntptrace \
+ ntptimeset ntptime ntp-genkeys
+
+.if !defined(NOMAN)
+SUBDIR+= doc
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/ntp/Makefile.inc b/usr.sbin/ntp/Makefile.inc
new file mode 100644
index 0000000..1f548ee
--- /dev/null
+++ b/usr.sbin/ntp/Makefile.inc
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+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 exists(${.OBJDIR}/../libparse)
+LIBPARSE= ${.OBJDIR}/../libparse/libparse.a
+.else
+LIBPARSE= ${.CURDIR}/../libparse/libparse.a
+.endif
+
+.if exists(${.OBJDIR}/../libntp)
+LIBNTP= ${.OBJDIR}/../libntp/libntp.a
+.else
+LIBNTP= ${.CURDIR}/../libntp/libntp.a
+.endif
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/ntp/config.h b/usr.sbin/ntp/config.h
new file mode 100644
index 0000000..68087af
--- /dev/null
+++ b/usr.sbin/ntp/config.h
@@ -0,0 +1,1058 @@
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.in by autoheader. */
+/* $FreeBSD$ */
+
+#define ULONG_CONST(a) a ## UL
+
+/* Is adjtime() accurate? */
+/* #undef ADJTIME_IS_ACCURATE */
+
+/* CHU audio/decoder? */
+/* #undef AUDIO_CHU */
+
+/* Autokey? */
+#define AUTOKEY
+
+/* 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 */
+
+/* EES M201 MSF receiver */
+/* #undef CLOCK_MSFEES */
+
+/* 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 */
+
+/* PTB modem service */
+/* #undef CLOCK_PTBACTS */
+
+/* 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 */
+
+/* TRAK 8810 GPS receiver */
+/* #undef CLOCK_TRAK */
+
+/* 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 */
+
+/* USNO modem service */
+/* #undef CLOCK_USNO */
+
+/* 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 */
+
+/* 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_MRAND48_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_SRAND48_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
+
+/* Use DES? */
+/* #undef DES */
+
+/* synch TODR hourly? */
+/* #undef DOSYNCTODR */
+
+/* The number of minutes in a DST adjustment */
+#define DSTMINUTES 60
+
+/* force ntpdate to step the clock if !defined(STEP_SLEW) ? */
+/* #undef FORCE_NTPDATE_STEP */
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+#define HAVE_ARPA_NAMESER_H 1
+
+/* Do we have audio support? */
+/* #undef HAVE_AUDIO */
+
+/* Define to 1 if you have the <bstring.h> header file. */
+/* #undef HAVE_BSTRING_H */
+
+/* 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 to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* 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 `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
+
+/* 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. */
+/* #undef HAVE_ISFINITE */
+
+/* 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 */
+
+/* Define to 1 if you have the `elf' library (-lelf). */
+/* #undef HAVE_LIBELF */
+
+/* Define to 1 if you have the `gen' library (-lgen). */
+/* #undef HAVE_LIBGEN */
+
+/* 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 `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). */
+#define HAVE_LIBREADLINE 1
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+/* #undef HAVE_LIBRT */
+
+/* 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 <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 `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. */
+/* #undef HAVE_MLOCKALL */
+
+/* Define to 1 if you have the `mrand48' function. */
+#define HAVE_MRAND48 1
+
+/* 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/if.h> header file. */
+#define HAVE_NET_IF_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 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 `random' function. */
+/* #undef HAVE_RANDOM */
+
+/* Define to 1 if you have the <readline/history.h> header file. */
+#define HAVE_READLINE_HISTORY_H 1
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+#define HAVE_READLINE_READLINE_H 1
+
+/* Define to 1 if you have the `readlink' function. */
+#define HAVE_READLINK 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 `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 `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. */
+#define HAVE_SGTTY_H 1
+
+/* 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
+
+/* Define to 1 if you have the `srand48' function. */
+#define HAVE_SRAND48 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 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 `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
+
+/* 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 <sys/audioio.h> header file. */
+/* #undef HAVE_SYS_AUDIOIO_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/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/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/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/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/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. */
+#define HAVE_SYS_SCHED_H 1
+
+/* 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/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/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/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
+
+/* 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. */
+/* #undef HAVE_TIMER_CREATE */
+
+/* Define to 1 if you have the `timer_settime' function. */
+/* #undef HAVE_TIMER_SETTIME */
+
+/* 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 */
+
+/* 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 <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 `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 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"
+
+/* 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 */
+
+/* Is there a problem using PARENB and IGNPAR (IRIX)? */
+#define NO_PARENB_IGNPAR 1
+
+/* Default location of crypto key info */
+#define NTP_KEYSDIR "/usr/local/etc"
+
+/* 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
+
+/* 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 ""
+
+/* 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 ""
+
+/* 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 if compiler has function prototypes */
+#define PROTOTYPES 1
+
+/* Public key? */
+/* #undef PUBKEY */
+
+/* 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
+
+/* Do we want the ReliantUNIX clock hacks? */
+/* #undef RELIANTUNIX_CLOCK */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Use RSAREF? */
+/* #undef RSAREF */
+
+/* Do we want the SCO clock hacks? */
+/* #undef SCO5_CLOCK */
+
+/* The size of an `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__)
+#define SIZEOF_LONG 8
+#else
+#define SIZEOF_LONG 4
+#endif
+
+/* The size of a `signed char', as computed by sizeof. */
+#define SIZEOF_SIGNED_CHAR 1
+
+/* Does SIOCGIFCONF return size in the buffer? */
+/* #undef SIZE_RETURNED_IN_BUFFER */
+
+/* Slew always? */
+/* #undef SLEWALWAYS */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Step, then slew the clock? */
+/* #undef STEP_SLEW */
+
+/* Do we have STREAMS/TLI? (Can we replace this with HAVE_SYS_STROPTS_H?) */
+/* #undef STREAMS_TLI */
+
+/* canonical system (cpu-vendor-os) string */
+#if defined(__alpha__)
+#define STR_SYSTEM "alpha-undermydesk-freebsd"
+#elif defined(__sparc64__)
+#define STR_SYSTEM "sparc64-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 */
+
+/* Do we set process groups with -pid? */
+/* #undef UDP_BACKWARDS_SETOWN */
+
+/* use UDP Wildcard Delivery? */
+#define UDP_WILDCARD_DELIVERY 1
+
+/* 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.1.1b"
+
+/* 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 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 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 as `__inline' if that's what the C compiler calls it, or to nothing
+ if it is not supported. */
+/* #undef inline */
+
+/* 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 */
+
+/* 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..b52cae4
--- /dev/null
+++ b/usr.sbin/ntp/doc/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+MAINTAINER= sheldonh
+
+FILESDIR= ${SHAREDIR}/doc/ntp
+
+FILES= accopt.htm assoc.htm audio.htm authopt.htm biblio.htm build.htm \
+ clockopt.htm \
+ config.htm confopt.htm copyright.htm debug.htm driver1.htm \
+ driver10.htm driver11.htm driver12.htm driver16.htm driver18.htm \
+ driver19.htm driver2.htm driver20.htm driver22.htm driver23.htm \
+ driver24.htm driver26.htm driver27.htm driver28.htm driver29.htm \
+ driver3.htm driver30.htm driver32.htm driver33.htm driver34.htm \
+ driver35.htm driver36.htm driver37.htm \
+ driver4.htm driver5.htm driver6.htm driver7.htm driver8.htm \
+ driver9.htm exec.htm extern.htm gadget.htm hints.htm \
+ howto.htm htmlprimer.htm index.htm kern.htm kernpps.htm \
+ ldisc.htm measure.htm miscopt.htm monopt.htm mx4200data.htm \
+ notes.htm ntpd.htm ntpdate.htm ntpdc.htm ntpq.htm ntptime.htm \
+ ntptrace.htm parsedata.htm parsenew.htm patches.htm porting.htm \
+ pps.htm prefer.htm qth.htm quick.htm rdebug.htm refclock.htm \
+ release.htm tickadj.htm
+
+MAN= ntp.conf.5 ntp.keys.5
+MAN+= ntp-genkeys.8 ntpd.8 ntpdate.8 ntpdc.8 ntpq.8 ntptime.8 ntptrace.8
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/html
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/doc/ntp-genkeys.8 b/usr.sbin/ntp/doc/ntp-genkeys.8
new file mode 100644
index 0000000..2b3bdd8
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp-genkeys.8
@@ -0,0 +1,208 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 2, 2001
+.Dt NTP_GENKEYS 8
+.Os
+.Sh NAME
+.Nm ntp-genkeys
+.Nd generate public and private keys
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfhlnt
+.Op Fl c Ar conffile
+.Op Fl g Ar target
+.Op Fl k Ar keyfile
+.Sh DESCRIPTION
+The
+.Nm
+utility generates random keys used by either or both the
+NTPv3/NTPv4 symmetric key or the NTPv4 public key (Autokey)
+cryptographic authentication schemes.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar conffile
+Location of
+.Xr ntp.conf 5
+file.
+.It Fl d
+enable debug messages (can be used multiple times)
+.It Fl f
+force installation of generated keys.
+.It Fl g target
+Generate file or files indicated by the characters in the
+.Ar target
+string:
+.Bl -tag -width X
+.It Li d
+Generate D-H parameter file.
+.It Li m
+Generate MD5 key file.
+.It Li r
+Generate RSA keys.
+.El
+.It Fl h
+Build keys here (current directory).
+Implies
+.Fl l .
+.It Fl k Ar keyfile
+Location of key file.
+.It Fl l
+Do not make the symlinks.
+.It Fl n
+Do not actually do anything, just say what would be done.
+.It Fl t
+Trash the (old) files at the end of symlink.
+.El
+.Pp
+By default the program
+generates the
+.Xr ntp.keys 5
+file containing 16 random symmetric
+keys.
+In addition, if the
+rsaref20
+package is configured
+for the software build, the program generates cryptographic values
+used by the Autokey scheme.
+These values are incorporated as a set
+of three files,
+.Pa ntpkey
+containing the RSA private key,
+.Pa ntpkey_ Ns Ar host
+containing the RSA public key, where
+.Ar host
+is the DNS name of the generating machine, and
+.Pa ntpkey_dh
+containing the parameters for the Diffie-Hellman
+key-agreement algorithm.
+All files and are in printable ASCII
+format.
+A timestamp in NTP seconds is appended to each.
+Since the
+algorithms are seeded by the system clock, each run of this program
+produces a different file and file name.
+.Pp
+The
+.Xr ntp.keys 5
+file contains 16 MD5 keys.
+Each key
+consists of 16 characters randomized over the ASCII 95-character
+printing subset.
+The file is read by the daemon at the location
+specified by the
+.Ic keys
+configuration file command and made
+visible only to root.
+An additional key consisting of an easily
+remembered password should be added by hand for use with the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs.
+The file must be
+distributed by secure means to other servers and clients sharing
+the same security compartment.
+While the key identifiers for MD5
+and DES keys must be in the range 1-65534, inclusive, the
+.Nm
+utility uses only the identifiers from 1 to
+16.
+The key identifier for each association is specified as the key
+argument in the
+.Ic server
+or
+.Ic peer
+configuration file command.
+.Pp
+The
+.Pa ntpkey
+file contains the RSA private key.
+It is
+read by the daemon at the location specified by the
+.Ar privatekey
+argument of the
+.Ic crypto
+configuration
+file command and made visible only to root.
+This file is useful
+only to the machine that generated it and never shared with any
+other daemon or application program.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+file contains the RSA public
+key, where
+.Ar host
+is the DNS name of the host that
+generated it.
+The file is read by the daemon at the location
+specified by the
+.Ar publickey
+argument to the
+.Ic server
+or
+.Ic peer
+configuration file command.
+This file can be
+widely distributed and stored without using secure means, since the
+data are public values.
+.Pp
+The
+.Pa ntp_dh
+file contains two Diffie-Hellman parameters:
+the prime modulus and the generator.
+The file is read by the daemon
+at the location specified by the
+.Ar dhparams
+argument of the
+.Ic crypto
+configuration file command.
+The file can be
+distributed by insecure means to other servers and clients sharing
+the same key agreement compartment, since the data are public
+values.
+.Pp
+The file formats begin with two lines, the first containing the
+generating system DNS name and the second the datestamp.
+Lines
+beginning with
+.Ql #
+are considered comments and ignored by
+the daemon.
+In the
+.Xr ntp.keys 5
+file, the next 16 lines
+contain the MD5 keys in order.
+If necessary, this file can be
+further customized by an ordinary text editor.
+The format is
+described in the following section.
+In the
+.Pa ntpkey
+and
+.Pa ntpkey_ Ns Ar host
+files, the next line contains the
+modulus length in bits followed by the key as a PEM encoded string.
+In the
+.Pa ntpkey_dh
+file, the next line contains the prime
+length in bytes followed by the prime as a PEM encoded string, and
+the next and final line contains the generator length in bytes
+followed by the generator as a PEM encoded string.
+.Pp
+Note: See the file
+.Pa ./source/rsaref.h
+in the
+rsaref20
+package for explanation of return values, if
+necessary.
+.Sh SEE ALSO
+.Xr ntp.keys 5 ,
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+.Sh BUGS
+It can take quite a while to generate the RSA public/private key
+pair and Diffie-Hellman parameters, from a few seconds on a modern
+workstation to several minutes on older machines.
diff --git a/usr.sbin/ntp/doc/ntp.conf.5 b/usr.sbin/ntp/doc/ntp.conf.5
new file mode 100644
index 0000000..1a20135
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp.conf.5
@@ -0,0 +1,2093 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 13, 2000
+.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 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 Reference Clock Support
+.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 (IP class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IP
+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.
+.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.
+.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 224.0.1.1 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 and at each poll interval, send a
+burst of eight packets instead of the usual one packet.
+The spacing
+between the first and the second packets is about 16s to allow a
+modem call to complete, while the spacing between the remaining
+packets is about 2s.
+This is designed to improve timekeeping
+quality with the
+.Ic server
+command and s
+addresses.
+.It Cm iburst
+When the server is unreachable and at each poll interval, send
+a burst of eight packets instead of the usual one.
+As long as the
+server is unreachable, the spacing between packets is about 16s to
+allow a modem call to complete.
+Once the server is reachable, the
+spacing between packets is about 2s.
+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, in seconds to the power of two.
+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 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 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
+.Cm ttl
+to
+use on broadcast server and multicast server and the maximum
+.Cm 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 augmented 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 schemes, 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.
+.Pp
+Authentication is configured separately for each association
+using the
+.Cm key
+or
+.Cm autokey
+subcommands on the
+.Ic peer ,
+.Ic server ,
+.Ic broadcast
+and
+.Ic manycastclient
+commands as described in
+.Sx Configuration Options .
+The authentication
+options described below specify the suite of keys, select the key
+for each configured association and manage the configuration
+operations.
+.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
+configuration 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 schemes.
+If
+this flag is disabled, these operations are effective even if not
+cryptographic authenticated.
+It should be understood that operating
+in the latter mode invites a significant vulnerability where a
+rogue hacker can seriously disrupt client timekeeping.
+.Pp
+In networks with firewalls and large numbers of broadcast
+clients it may be acceptable to disable authentication, since that
+avoids key distribution and simplifies network maintenance.
+However, when the configuration file contains host names, or when a
+server or client is configured remotely, host names are resolved
+using the DNS and a separate name resolution process.
+In order to
+protect against bogus name server messages, name resolution
+messages are authenticated using an internally generated key which
+is normally invisible to the user.
+However, if cryptographic
+support is disabled, the name resolution process will fail.
+This
+can be avoided either by specifying IP addresses instead of host
+names, which is generally inadvisable, or by enabling the flag for
+name resolution and disabled it once the name resolution process is
+complete.
+.Pp
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll for servers.
+Cryptographic authentication in this mode uses public-key schemes
+as described below.
+The principle advantage of this 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
+In addition to the default symmetric-key cryptographic support,
+support for public-key cryptography is available if the requisite
+.Sy rsaref20
+software distribution has been installed before
+building the distribution.
+Public-key cryptography provides secure
+authentication of servers without compromising accuracy and
+stability.
+The security model and protocol schemes for both
+symmetric-key and public-key cryptography are described below.
+.Ss Symmetric-Key Scheme
+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 their messages.
+Keys and related information are specified in a key file, usually
+called
+.Pa ntp.keys ,
+which should be exchanged and stored
+using secure procedures 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
+command and installs the keys in the
+key cache.
+However, the 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 Scheme
+The original NTPv3 authentication scheme described in RFC-1305
+continues to be supported; however, in NTPv4 an additional
+authentication scheme called Autokey is available.
+It uses MD5
+message digest, RSA public-key signature and Diffie-Hellman key
+agreement algorithms available from several sources, but not
+included in the NTPv4 software distribution.
+In order to be
+effective, the
+.Sy rsaref20
+package must be installed as
+described in the
+.Pa README.rsa
+file.
+Once installed, the
+configure and build process automatically detects it and compiles
+the routines required.
+The Autokey scheme has several modes of
+operation corresponding to the various NTP modes supported.
+RSA
+signatures with timestamps are used in all modes to verify the
+source of cryptographic values.
+All modes use a special cookie
+which can be computed independently by the client and server.
+In
+symmetric modes the cookie is constructed using the Diffie-Hellman
+key agreement algorithm.
+In other modes the cookie is constructed
+from the IP addresses and a private value known only to the server.
+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, in the
+.Qq "Autonomous Authentication"
+page.
+.Pp
+The cryptographic values used by the Autokey scheme are
+incorporated as a set of files generated by the
+.Xr ntp-genkeys 8
+program, including the
+symmetric private keys, public/private key pair, and the agreement
+parameters.
+See the
+.Xr ntp.keys 5
+page for a description of
+the formats of these files.
+They contain cryptographic values
+generated by the algorithms of the
+.Sy rsaref20
+package and
+are in printable ASCII format.
+All file names include the
+timestamp, in NTP seconds, following the default names given below.
+Since the file data are derived from random values seeded by the
+system clock and the file name includes the timestamp, every
+generation produces a different file and different file name.
+.Pp
+The
+.Pa ntp.keys
+file contains the DES/MD5 private keys.
+It
+must be distributed by secure means to other servers and clients
+sharing the same security compartment and made visible only to
+root.
+While this file is not used with the Autokey scheme, it is
+needed to authenticate some remote configuration commands used by
+the
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+utilities.
+The
+.Pa ntpkey
+file
+contains the RSA private key.
+It is useful only to the machine that
+generated it and never shared with any other daemon or application
+program, so must be made visible only to root.
+.Pp
+The
+.Pa ntp_dh
+file contains the agreement parameters,
+which are used only in symmetric (active and passive) modes.
+It is
+necessary that both peers beginning a symmetric-mode association
+share the same parameters, but it does not matter which
+.Pa ntp_dh
+file generates them.
+If one of the peers contains
+the parameters, the other peer obtains them using the Autokey
+protocol.
+If both peers contain the parameters, the most recent
+copy is used by both peers.
+If a peer does not have the parameters,
+they will be requested by all associations, either configured or
+not; but, none of the associations can proceed until one of them
+has received the parameters.
+Once loaded, the parameters can be
+provided on request to other clients and servers.
+The
+.Pa ntp_dh
+file can be also be distributed using insecure
+means, since the data are public values.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+file contains the RSA public
+key, where
+.Ar host
+is the name of the host.
+Each host
+must have its own
+.Pa ntpkey_ Ns Ar host
+file, which is
+normally provided to other hosts using the Autokey protocol.
+Each
+.Ic server
+or
+.Ic peer
+association requires the public
+key associated with the particular server or peer to be loaded
+either directly from a local file or indirectly from the server
+using the Autokey protocol.
+These files can be widely distributed
+and stored using insecure means, since the data are public
+values.
+.Pp
+The optional
+.Pa ntpkey_certif_ Ns Ar host
+file contains
+the PKI certificate for the host.
+This provides a binding between
+the host hame and RSA public key.
+In the current implementation the
+certificate is obtained by a client, if present, but the contents
+are ignored.
+.Pp
+Due to the widespread use of interface-specific naming, the host
+names used in configured and mobilized associations are determined
+by the
+.Ux
+.Xr gethostname 3
+library routine.
+Both the
+.Xr ntp-genkeys 8
+program and the Autokey protocol derive the
+name of the public key file using the name returned by this
+routine.
+While every server and client is required to load their
+own public and private keys, the public keys for each client or
+peer association can be obtained from the server or peer using the
+Autokey protocol.
+Note however, that at the current stage of
+development the authenticity of the server or peer and the
+cryptographic binding of the server name, address and public key is
+not yet established by a certificate authority or web of trust.
+.Ss Leapseconds Table
+The NIST provides a table showing the epoch for all historic
+occasions of leap second insertion since 1972.
+The leapsecond table
+shows each epoch of insertion along with the offset of
+International Atomic Time (TAI) with respect to Coordinated
+Universal Time (UTC), as disseminated by NTP.
+The table can be
+obtained directly from NIST national time servers using
+FTP as the ASCII file
+.Pa pub/leap-seconds .
+.Pp
+While not strictly a security function, the Autokey scheme
+provides means to securely retrieve the leapsecond table from a
+server or peer.
+Servers load the leapsecond table directly from the
+file specified in the
+.Ic crypto
+command, while clients can
+load the table indirectly from the servers using the Autokey
+protocol.
+Once loaded, the table can be provided on request to
+other clients and servers.
+.Ss Key Management
+All key files are installed by default in
+.Pa /usr/local/etc ,
+which is normally in a shared file system
+in NFS-mounted networks and avoids installing them in each machine
+separately.
+The default can be overridden by the
+.Ic keysdir
+configuration command.
+However, this is not a good place to install
+the private key file, since each machine needs its own file.
+A
+suitable place to install it is in
+.Pa /etc ,
+which is normally
+not in a shared file system.
+.Pp
+The recommended practice is to keep the timestamp extensions
+when installing a file and to install a link from the default name
+(without the timestamp extension) to the actual file.
+This allows
+new file generations to be activated simply by changing the link.
+However,
+.Xr ntpd 8
+parses the link name when present to extract
+the extension value and sends it along with the public key and host
+name when requested.
+This allows clients to verify that the file
+and generation time are always current.
+However, the actual
+location of each file can be overridden by the
+.Ic crypto
+configuration command.
+.Pp
+All cryptographic keys and related parameters should be
+regenerated on a periodic and automatic basis, like once per month.
+The
+.Xr ntp-genkeys 8
+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.
+While a
+public/private key pair must be generated by every server and
+client, the public keys and agreement parameters do not need to be
+explicitly copied to all machines in the same security compartment,
+since they can be obtained automatically using the Autokey
+protocol.
+However, it is necessary that all primary servers have
+the same agreement parameter file.
+The recommended way to do this
+is for one of the primary servers to generate that file and then
+copy it to the other primary servers in the same compartment using
+the
+.Ux
+.Xr rdist 1
+command.
+Future versions of the Autokey
+protocol are to contain provisions for an agreement protocol to do
+this automatically.
+.Pp
+Servers and clients can make a new generation in the following
+way.
+All machines have loaded the old generation at startup and are
+operating normally.
+At designated intervals, each machine generates
+a new public/private key pair and makes links from the default file
+names to the new file names.
+The
+.Xr ntpd 8
+is then restarted
+and loads the new generation, with result clients no longer can
+authenticate correctly.
+The Autokey protocol is designed so that
+after a few minutes the clients time out and restart the protocol
+from the beginning, with result the new generation is loaded and
+operation continues as before.
+A similar procedure can be used for
+the agreement parameter file, but in this case precautions must be
+take to be sure that all machines with this file have the same
+copy.
+.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 65534, inclusive.
+.It Xo Ic crypto
+.Op Cm flags Ar flags
+.Op Cm privatekey Ar file
+.Op Cm publickey Ar file
+.Op Cm dhparms Ar file
+.Op Cm leap Ar file
+.Xc
+This command requires the NTP daemon build process be
+configured with the RSA library.
+This command activates public-key
+cryptography and loads the required RSA private and public key
+files and the optional Diffie-Hellman agreement parameter file, if
+present.
+If one or more files are left unspecified, the default
+names are used as described below.
+Following are the
+subcommands:
+.Bl -tag -width indent
+.It Cm privatekey Ar file
+Specifies the location of the RSA private key file, which
+otherwise defaults to
+.Pa /usr/local/etc/ntpkey .
+.It Cm publickey Ar file
+Specifies the location of the RSA public key file, which
+otherwise defaults to
+.Pa /usr/local/etc/ntpkey_ Ns Ar host ,
+where
+.Ar host
+is the name of the generating machine.
+.It Cm dhparms Ar file
+Specifies the location of the Diffie-Hellman parameters file,
+which otherwise defaults to
+.Pa /usr/local/etc/ntpkey_dh .
+.It Cm leap Ar file
+Specifies the location of the leapsecond table file, which
+otherwise defaults to
+.Pa /usr/local/etc/ntpkey_leap .
+.El
+.It Ic keys Ar keyfile
+Specifies the location of the DES/MD5 private key file
+containing the keys and key identifiers used by
+.Xr ntpd 8 ,
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+when operating in symmetric-key
+mode.
+.It Ic keysdir Ar path
+This command requires the NTP daemon build process be
+configured with the RSA library.
+It specifies the default directory
+path for the private key file, agreement parameters file and one or
+more public key files.
+The default when this command does not
+appear in the configuration file 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
+65534, 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
+.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 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 loopstats:
+.Bd -literal
+50935 75440.031 0.000006019 13.778190 0.000351733 0.013380 6
+.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 peerstats:
+.Bd -literal
+48773 10847.650 127.127.4.1 9714 -0.001605 0.00000 0.00142
+.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 three fields show the offset, delay and RMS jitter, all in
+seconds.
+.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 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 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 rawstats:
+.Bd -literal
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.Ed
+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.
+.El
+.It Ic 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)
+.Ic filegen
+filename prefix to be modified for file
+generation sets, which is useful for handling statistics logs.
+.It Xo Ic filegen Ar name
+.Op Cm file Ar filename
+.Op Cm type Ar typename
+.Op Cm link \&| Cm nolink
+.Op Cm enable \&| Cm disable
+.Xc
+Configures setting of generation file set
+.Ar 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
+.Xr ntpd 8 .
+(Most important: they can be removed to
+free space for new data produced.)
+Note that this command can be sent from the
+.Xr ntpdc 8
+program running at a remote location.
+.Bl -tag -width indent
+.It Ar name
+This is the type of the statistics records, as shown in the
+.Ic 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
+prefix, filename and
+suffix:
+.Bl -tag -width indent
+.It prefix
+This is a constant filename path.
+It is not subject to
+modifications via the
+.Ic 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
+.Cm loopstats
+and
+.Cm peerstats
+generation can be
+configured using the
+.Ic statsdir
+option explained above.
+.It filename
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+.Ql /
+(slash)).
+This can be modified
+using the
+.Ar file
+argument to the
+.Ic filegen
+statement.
+No
+.Ql \&..
+elements are allowed in this component to prevent
+filenames referring to parts outside the file system hierarchy
+denoted by prefix.
+.It 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 none
+The file set is actually a single plain file.
+.It pid
+One element of file set is used per incarnation of a
+.Xr ntpd 8
+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 \&.
+(dot) to concatenated prefix and filename
+strings, and appending the decimal representation of the process ID
+of the
+.Xr ntpd 8
+server process.
+.It 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 \&.
+(dot) and a day
+specification in the form
+.Ar YYYYMMdd .
+.Ar YYYY
+is a 4-digit year
+number (e.g., 1992).
+.Ar MM
+is a two digit month number.
+.Ar dd
+is a two digit day number.
+Thus, all information
+written at 10 December 1992 would end up in a file named
+.Sm off
+.Pa Ar prefix / Ar filename / 19921210 .
+.Sm on
+.It 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
+Ql W ,
+and a 2-digit
+week number.
+For example, information from January, 10th 1992 would
+end up in a file with suffix
+.Pa .1992W1 .
+.It 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 year
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.It 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
+.Ql 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
+.Ic enable ;
+output is prevented by specifying
+.Ic disable .
+.El
+.It Cm link \&| Cm 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
+.Cm 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
+.Ql C ,
+and the pid
+of the
+.Xr ntpd 8
+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
+.Sh Access Control Support
+.Xr ntpd 8
+implements a general purpose address-and-mask based
+restriction list.
+The list is sorted by address and by mask, and
+the list is searched in this order for matches, with the last match
+found defining the restriction flags associated with the incoming
+packets.
+The source address of incoming packets is used for the
+match, with the 32- bit address being and'ed with the mask
+associated with the restriction entry and then compared with the
+entry's address (which has also been and'ed with the mask) to look
+for a match.
+Additional information and examples can be found in the
+.Qq "Notes on Configuring NTP and Setting up a NTP Subnet"
+page.
+.Pp
+The restriction facility was implemented in conformance with the
+access policies for the original NSFnet backbone time servers.
+While this facility may be otherwise useful for keeping unwanted or
+broken remote time servers from affecting your own, it should not
+be considered an alternative to the standard NTP authentication
+facility.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.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 packet.
+If the
+.Cm kod
+flag is set and either service is denied or the client
+limit is exceeded, the server returns the packet and sets the
+leap bits unsynchronized, stratum zero and the ASCII string "DENY"
+in the reference source identifier field.
+If the
+.Cm kod
+flag
+is not set, the server simply drops the packet.
+.Pp
+A client or peer receiving a kiss-of-death packet performs a set
+of sanity checks to minimize security exposure.
+If this is the
+first packet received from the server, the client assumes an access
+denied condition at the server.
+It updates the stratum and
+reference identifier peer variables and sets the access denied
+(test 4) bit in the peer flash variable.
+If this bit is set, the
+client sends no packets to the server.
+If this is not the first
+packet, the client assumes a client limit condition at the server,
+but does not update the peer variables.
+In either case, a message
+is sent to the system log.
+.Ss Access Control Commands
+.Bl -tag -width indent
+.It Xo Ic restrict numeric_address
+.Op Cm mask Ar numeric_mask
+.Op Ar flag ...
+.Xc
+The
+.Ar numeric_address
+argument, expressed in
+dotted-quad form, is the address of a host or network.
+The
+.Cm mask ,
+also expressed in dotted-quad form,
+defaults to 255.255.255.255, meaning that the
+.Ar numeric_address
+is treated as the address of an
+individual host.
+A default entry (address 0.0.0.0, mask
+0.0.0.0) is always included and, given the sort algorithm,
+is always the first entry in the list.
+Note that, while
+.Ar numeric_address
+is normally given in dotted-quad
+format, the text string
+.Ql 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 kod
+If access is denied, send a kiss-of-death packet.
+.It Cm ignore
+Ignore all packets from hosts which match this entry.
+If this
+flag is specified neither queries nor time server polls will be
+responded to.
+.It Cm noquery
+Ignore all NTP mode 6 and 7 packets (i.e. information queries
+and configuration requests) from the source.
+Time service is not
+affected.
+.It Cm nomodify
+Ignore all NTP mode 6 and 7 packets which attempt to modify the
+state of the server (i.e. run time reconfiguration).
+Queries which
+return information are permitted.
+.It Cm notrap
+Decline to provide mode 6 control message trap service to
+matching hosts.
+The trap service is a subsystem of the mode 6
+control message protocol which is intended for use by remote event
+logging programs.
+.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 noserve
+Ignore NTP packets whose mode is other than 6 or 7.
+In effect,
+time service is denied, though queries may still be permitted.
+.It Cm nopeer
+Provide stateless time service to polling hosts, but do not
+allocate peer memory resources to these hosts even if they
+otherwise might be considered useful as future synchronization
+partners.
+.It Cm notrust
+Treat these hosts normally in other respects, but never use
+them as synchronization sources.
+.It Cm limited
+These hosts are subject to limitation of number of clients from
+the same net.
+Net in this context refers to the IP notion of net
+(class A, class B, class C, etc.).
+Only the first
+.Va client_limit
+hosts that have shown up at the server and
+that have been active during the last
+.Va client_limit_period
+seconds are accepted.
+Requests from other clients from the same net
+are rejected.
+Only time request packets are taken into account.
+Query packets sent by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs
+are not subject to these limits.
+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 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
+Ignore these hosts if not the current NTP version.
+.El
+.Pp
+Default restriction list entries, with the flags
+.Cm ignore ,
+.Cm interface ,
+.Cm 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).
+.It Ic clientlimit Ar limit
+Set the
+.Va client_limit
+variable, which limits the number
+of simultaneous access-controlled clients.
+The default value for
+this variable is 3.
+.It Ic clientperiod Ar period
+Set the
+.Va client_limit_period
+variable, which specifies
+the number of seconds after which a client is considered inactive
+and thus no longer is counted for client limit restriction.
+The
+default value for this variable is 3600 seconds.
+.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.
+In addition, support for a PPS
+signal is available as described in the
+.Qq "Pulse-per-second (PPS) Signal Interfacing"
+page.
+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.
+.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"
+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 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, in seconds to the power of two.
+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.
+.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.
+.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 driftfile Ar driftfile
+This command specifies the name of the file used to record the
+frequency offset of the local clock oscillator.
+If the file exists,
+it is read at startup in order to set the initial frequency offset
+and then updated once per hour with the current frequency offset
+computed by the daemon.
+If the file does not exist or this command
+is not given, the initial frequency offset is assumed zero.
+In this
+case, it may take some hours for the frequency to stabilize and the
+residual timing errors to subside.
+.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 stats
+.Oc
+.Xc
+.It Xo Ic disable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm monitor | Cm ntp |
+.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 bclient
+When enabled, this is identical to the
+.Ic broadcastclient
+command.
+The default for this flag is
+.Ic disable .
+.It Cm calibrate
+Enables the calibration facility, which automatically adjusts
+the
+.Ic time1
+values for each clock driver to display the same
+offset as the currently selected source or kernel discipline
+signal.
+See the
+.Qq "Reference Clock Drivers"
+page
+for further information.
+The default for this flag is
+.Ic disable .
+.It Cm kernel
+Enables the precision-time kernel support for the
+.Xr adjtime 2
+system call, if implemented.
+Ordinarily,
+support for this routine is detected automatically when the NTP
+daemon is compiled, so it is not necessary for the user to worry
+about this flag.
+It is provided primarily so that this support
+can be disabled during kernel development.
+The default for this
+flag is
+.Ic enable .
+.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 the server to adjust its local clock by means of NTP.
+If disabled, the local clock free-runs at its intrinsic time and
+frequency offset.
+This flag is useful in case the local clock is
+controlled by some other device or protocol and NTP is used only to
+provide synchronization to other clients.
+In this case, the local
+clock driver can be used to provide this function and also certain
+time variables for error estimates and leap-indicators.
+See the
+.Qq "Reference Clock Drivers"
+page for further
+information.
+The default for this flag is
+.Ic enable .
+.It Cm stats
+Enables the statistics facility.
+See the
+.Qq "Monitoring Options"
+page for further information.
+The default for this flag is
+.Ic enable .
+.El
+.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
+.Pq Cm info
+control configuration
+information.
+Event messages
+.Pq Cm events
+control logging of
+events (reachability, synchronization, alarm conditions).
+Statistical output is controlled with the
+.Cm statistics
+keyword.
+The final message group is the status messages.
+This
+describes mainly the synchronizations status.
+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.
+.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 step Ar step |
+.Cm panic Ar panic |
+.Cm dispersion Ar dispersion |
+.Cm stepout Ar stepout |
+.Cm minpoll Ar minpoll |
+.Cm allan Ar allan |
+.Cm huffpuff Ar huffpuff
+.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 can't 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
+All arguments are in floating point seconds or seconds per
+second.
+The
+.Ar minpoll
+argument is an integer in seconds to
+the power of two.
+The variables operate as follows:
+.Bl -tag -width indent
+.It Cm step Ar step
+The argument becomes the new value for the step threshold,
+normally 0.128 s.
+If set to zero, step adjustments will never
+occur.
+In general, if the intent is only to avoid step adjustments,
+the step threshold should be left alone and the
+.Fl x
+command
+line option be used instead.
+.It Cm panic Ar panic
+The argument becomes the new value for 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 dispersion Ar dispersion
+The argument becomes the new value for the dispersion increase
+rate, normally .000015.
+.It Cm stepout Ar stepout
+The argument becomes the new value for the watchdog timeout,
+normally 900 s.
+.It Cm minpoll Ar minpoll
+The argument becomes the new value for the minimum poll
+interval used when configuring multicast client, manycast client
+and , symmetric passive mode association.
+The value defaults to 6
+(64 s) and has a lower limit of 4 (16 s).
+.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 defaults to 1024 s, which is also the lower
+limit.
+.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.
+.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.
+.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 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..0f56467
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpd.8
@@ -0,0 +1,588 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 2, 2001
+.Dt NTPD 8
+.Os
+.Sh NAME
+.Nm ntpd
+.Nd Network Time Protocol (NTP) daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl aAbdgLmnPqx
+.Op Fl c Ar conffile
+.Op Fl D Ar level
+.Op Fl f Ar driftfile
+.Op Fl k Ar keyfile
+.Op Fl l Ar logfile
+.Op Fl N Cm high
+.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
+.Xr umask 2 ,
+and if zero
+.Nm
+will set the
+.Xr umask 2
+to 022.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Enable authentication mode (default).
+.It Fl A
+Disable authentication mode.
+.It Fl b
+Synchronize using NTP broadcast messages.
+.It Fl c Ar conffile
+Specify the name and path of the configuration file.
+(Disable
+netinfo?)
+.It Fl d
+Specify debugging mode.
+This flag may occur multiple times,
+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 drift file.
+.It Fl g
+Normally,
+.Nm
+exits if the offset exceeds the sanity
+limit, which is 1000 s by default.
+If the sanity limit is set to
+zero, no sanity checking is performed and any offset is acceptable.
+This option overrides the limit and allows the time to be set to
+any value without restriction; however, this can happen only once.
+After that,
+.Nm
+will exit if the limit is exceeded.
+This
+option can be used with the
+.Fl q
+option.
+.It Fl k Ar keyfile
+Specify the name and path of the file containing the NTP
+authentication keys.
+.It Fl l Ar logfile
+Specify the name and path of the log file.
+The default is the
+system log facility.
+.It Fl L
+Listen to virtual IPs.
+.It Fl m
+Synchronize using NTP multicast messages on the IP multicast
+group address 224.0.1.1 (requires multicast kernel).
+.It Fl n
+Don't fork.
+.It Fl N Ar priority
+To the extent permitted by the operating system, run the
+.Nm
+at a high priority.
+.It Fl p Ar pidfile
+Specify the name and path to record the
+.Nm Ns 's
+process
+ID.
+.It Fl P
+Override the priority limit set by the operating system.
+Not
+recommended for sissies.
+.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.
+.It Fl r Ar broadcastdelay
+Specify the default propagation delay from the
+broadcast/multicast server and this computer.
+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.
+.It Fl t Ar key
+Add a key number to the trusted key list.
+.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 forces the time to be slewed in all
+cases.
+If the step threshold is set to zero, all offsets are
+stepped, regardless of value and regardless of the
+.Fl x
+option.
+In general, this is not a good idea, as it bypasses the
+clock state machine which is designed to cope with large time and
+frequency errors Note: Since the slew rate is limited to 0.5 ms/s,
+each second of adjustment requires an amortization interval of 2000
+s.
+Thus, an adjustment of many seconds can take hours or days to
+amortize.
+This option can be used with the
+.Fl q
+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 0-16s.
+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
+(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 a minute.
+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..78f69ea
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpdate.8
@@ -0,0 +1,267 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 6, 2000
+.Dt NTPDATE 8
+.Os
+.Sh NAME
+.Nm ntpdate
+.Nd set the date and time via NTP
+.Sh SYNOPSIS
+.Nm
+.Op Fl bBdoqsuv
+.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 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 outgoint 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 - don't 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
+If NetInfo support is compiled into
+.Nm ,
+then the
+.Ic 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..e04020c
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpdc.8
@@ -0,0 +1,725 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2000
+.Dt NTPDC 8
+.Os
+.Sh NAME
+.Nm ntpdc
+.Nd special NTP query program
+.Sh SYNOPSIS
+.Nm
+.Op Fl ilnps
+.Op Fl c Ar command
+.Op Ar host ...
+.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 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 -c 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 -c 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 -c 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.
+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
+.Ic \&?
+will print a list of all the command
+keywords known to this incarnation of
+.Nm .
+A
+.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
+synchonization 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 ...
+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 ...
+Show per-peer statistic counters associated with the specified
+peer(s).
+.It Ic clockinfo Ar clock_peer_address ...
+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 ...
+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 ...
+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 Ic enable Ar flag ...
+.It Ic disable Ar flag ...
+These commands operate in the same way as the
+.Ic enable
+and
+.Ic disable
+configuration file commands of
+.Xr ntpd 8 .
+Following is a description of the flags.
+Note that only the
+.Cm auth ,
+.Cm bclient ,
+.Cm monitor ,
+.Cm pll ,
+.Cm pps
+and
+.Cm stats
+flags can be set by
+.Nm ;
+the
+.Cm pll_kernel
+and
+.Cm pps_kernel
+flags are
+read-only.
+.Bl -tag -width indent
+.It Cm auth
+Enables the server to synchronize with unconfigured peers only
+if the peer has been correctly authenticated using a trusted key
+and key identifier.
+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
+.Ic multicastclient
+command with
+default address.
+The default for this flag is disable.
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Ic monlist
+command for further information.
+The
+default for this flag is enable.
+.It Cm pll
+Enables the server to adjust its local clock by means of NTP.
+If disabled, the local clock free-runs at its intrinsic time and
+frequency offset.
+This flag is useful in case the local clock is
+controlled by some other device or protocol and NTP is used only to
+provide synchronization to other clients.
+In this case, the local
+clock driver is used.
+See the
+.Qq "Reference Clock Drivers"
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page for further information.
+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"
+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 the
+.Xr ntp.conf 5
+page for further information.
+The default for this flag is enable.
+.It Cm pll_kernel
+When the precision time kernel modifications are installed,
+this indicates the kernel controls the clock discipline; otherwise,
+the daemon controls the clock discipline.
+.It Cm pps_kernel
+When the precision time kernel modifications are installed and
+a pulse-per-second (PPS) signal is available, this indicates the
+PPS signal controls the clock discipline; otherwise, the daemon or
+kernel controls the clock discipline, as indicated by the
+.Cm pll_kernel
+flag.
+.El
+.It Xo Ic restrict Ar address Ar mask
+.Ar flag ...
+.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 ...
+.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 ...
+.It Ic untrustedkey Ar keyid ...
+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..266dfec
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpq.8
@@ -0,0 +1,749 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2000
+.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 ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to query NTP servers
+which implement the recommended NTP mode 6 control message format
+about current state and to request changes in that state.
+The
+program may 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
+utility can also obtain and print a
+list of peers in a common format by sending multiple 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 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 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
+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
+.Ql \&?
+by itself will print a list of all the command
+keywords known to this incarnation of
+.Nm .
+A
+.Ql \&?
+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 authenticate Cm yes | Cm no
+Normally
+.Nm
+does not authenticate requests unless
+they are write requests.
+The command
+.Ql authenticate yes
+causes
+.Nm
+to send authentication with all requests it
+makes.
+Authenticated requests causes some servers to handle
+requests slightly differently, and can occasionally melt the CPU in
+fuzzballs if you turn authentication on before doing a
+.Ic peer
+display.
+.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 didn't 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 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 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) didn't exist in NTP version 1.
+There appear
+to be no servers left which demand version 1.
+.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 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 peer known to an NTP server has a 16 bit integer association
+identifier assigned to it.
+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 of 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 in the left margin indicates the fate of this
+peer 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 candidate
+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 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
+.El
+.Pp
+The
+.Va flash
+variable is a valuable debugging aid.
+It
+displays the results of the original sanity checks defined in the
+NTP specification RFC-1305 and additional ones added in NTP Version
+4.
+There are eleven tests called
+.Sy TEST1
+through
+.Sy TEST11 .
+The tests are performed in a certain order
+designed to gain maximum diagnostic information while protecting
+against accidental or malicious errors.
+The
+.Va flash
+variable
+is first initialized to zero.
+If after each set of tests one or
+more bits are set, the packet is discarded.
+.Pp
+Tests
+.Sy TEST4
+and
+.Sy TEST5
+check the access
+permissions and cryptographic message digest.
+If any bits are set
+after that, the packet is discarded.
+Tests
+.Sy TEST10
+and
+.Sy TEST11
+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
+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.
+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.
+Test
+.Sy 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
+The
+.Va flash
+bits for each test read in increasing order
+from the least significant bit are defined as follows.
+.Bl -tag -width indent
+.It Sy TEST1
+Duplicate packet.
+The packet is at best a casual retransmission
+and at worst a malicious replay.
+.It Sy 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 Sy TEST3
+Unsynchronized.
+One or more timestamp fields are invalid.
+This
+normally happens when the first packet from a peer is
+received.
+.It Sy TEST4
+Access is denied.
+See the
+.Qq "Access Control"
+page.
+.It Sy TEST5
+Cryptographic authentication fails.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.It Sy TEST6
+The server is unsynchronized.
+Wind up its clock first.
+.It Sy TEST7
+The server stratum is at the maximum than 15.
+It is probably
+unsynchronized and its clock needs to be wound up.
+.It Sy TEST8
+Either the root delay or dispersion is greater than one second,
+which is highly unlikely unless the peer is synchronized to
+Mars.
+.It Sy TEST9
+Either the peer delay or dispersion is greater than one second,
+which is highly unlikely unless the peer is on Mars.
+.It Sy TEST10
+The autokey protocol has detected an authentication failure.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.It Sy TEST11
+The autokey protocol has not verified the server or peer is
+authentic and has valid public key credentials.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.El
+.Pp
+Additional system variables used by the NTP Version 4 Autokey
+support include the following:
+.Bl -tag -width indent
+.It Ic certificate Ar filestamp
+Shows the NTP seconds when the certificate file was
+created.
+.It Ic hostname Ar host
+Shows the name of the host as returned by the Unix
+.Xr gethostname 3
+library function.
+.It Ic flags Ar hex
+Shows the current flag bits, where the
+.Ar hex
+bits
+are interpreted as follows:
+.Bl -tag -width indent
+.It 0x01
+autokey enabled
+.It 0x02
+RSA public/private key files present
+.It 0x04
+PKI certificate file present
+.It 0x08
+Diffie-Hellman parameters file present
+.It 0x10
+NIST leapseconds table file present
+.El
+.It Ic leapseconds Ar filestamp
+Shows the NTP seconds when the NIST leapseconds table file was
+created.
+.It Ic params Ar filestamp
+Shows the NTP seconds when the Diffie-Hellman agreement
+parameter file was created.
+.It Ic publickey Ar filestamp
+Shows the NTP seconds when the RSA public/private key files
+were created.
+.It Ic refresh Ar filestamp
+Shows the NTP seconds when the public cryptographic values were
+refreshed and signed.
+.It Ic tai Ar offset
+Shows the TAI-UTC offset in seconds obtained from the NIST
+leapseconds table.
+.El
+.Pp
+Additional peer variables used by the NTP Version 4 Autokey
+support include the following:
+.Bl -tag -width indent
+.It Ic certificate Ar filestamp
+Shows the NTP seconds when the certificate file was
+created.
+.It Ic flags Ar hex
+Shows the current flag bits, where the
+.Ar hex
+bits are
+interpreted as in the system variable of the same name.
+The bits
+are set in the first autokey message received from the server and
+then reset as the associated data are obtained from the server and
+stored.
+.It Ic hcookie Ar hex
+Shows the host cookie used in the key agreement algorithm.
+.It Ic initkey Ar key
+Shows the initial key used by the key list generator in the
+autokey protocol.
+.It Ic initsequence Ar index
+Shows the initial index used by the key list generator in the
+autokey protocol.
+.It Ic pcookie Ar hex
+Specifies the peer cookie used in the key agreement
+algorithm.
+.It Ic timestamp Ar time
+Shows the NTP seconds when the last autokey key list was
+generated and signed.
+.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.
+.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
+.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 doesn't.
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..82e1ff3
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntptrace.8
@@ -0,0 +1,74 @@
+.\"
+.\" $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..37ef176
--- /dev/null
+++ b/usr.sbin/ntp/libntp/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/libntp
+
+LIB= ntp
+INTERNALLIB= true
+
+SRCS= a_md5encrypt.c adjtime.c atoint.c atolfp.c \
+ atouint.c audio.c authencrypt.c authkeys.c \
+ authparity.c authreadkeys.c authusekey.c binio.c \
+ buftvtots.c caljulian.c calleapwhen.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 gpstolfp.c \
+ hextoint.c hextolfp.c humandate.c icom.c \
+ ieee754io.c inttoa.c iosignal.c lib_strbuf.c \
+ machines.c md5c.c memmove.c mfp_mul.c \
+ mfptoa.c mfptoms.c modetoa.c mstolfp.c \
+ msutotsf.c msyslog.c netof.c numtoa.c \
+ numtohost.c octtoint.c prettydate.c ranny.c \
+ recvbuff.c refnumtoa.c statestr.c syssignal.c \
+ systime.c tsftomsu.c tstotv.c tvtoa.c \
+ tvtots.c uglydate.c uinttoa.c utvtoa.c \
+ ymd2yd.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/ntp/libparse/Makefile b/usr.sbin/ntp/libparse/Makefile
new file mode 100644
index 0000000..10b9796
--- /dev/null
+++ b/usr.sbin/ntp/libparse/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/libparse
+
+LIB= parse
+INTERNALLIB= true
+
+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
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/ntp/ntp-genkeys/Makefile b/usr.sbin/ntp/ntp-genkeys/Makefile
new file mode 100644
index 0000000..10a8106
--- /dev/null
+++ b/usr.sbin/ntp/ntp-genkeys/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/util \
+ ${.CURDIR}/../../../contrib/ntp/ntpd
+
+PROG= ntp-genkeys
+NOMAN= yes
+SRCS= ntp-genkeys.c ntp_config.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpd/Makefile b/usr.sbin/ntp/ntpd/Makefile
new file mode 100644
index 0000000..fc97856
--- /dev/null
+++ b/usr.sbin/ntp/ntpd/Makefile
@@ -0,0 +1,39 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpd
+
+PROG= ntpd
+NOMAN= yes
+SRCS= cmd_args.c map_vme.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_ptbacts.c refclock_ripencc.c \
+ refclock_shm.c refclock_tpro.c refclock_trak.c refclock_true.c \
+ refclock_ulink.c refclock_usno.c refclock_wwv.c \
+ refclock_wwvb.c \
+ version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBPARSE} ${LIBNTP} ${LIBM}
+LDADD= ${LIBPARSE} ${LIBNTP} -lm
+
+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..af0db44
--- /dev/null
+++ b/usr.sbin/ntp/ntpdate/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdate
+
+PROG= ntpdate
+NOMAN= yes
+SRCS= ntpdate.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+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..d17f0ae
--- /dev/null
+++ b/usr.sbin/ntp/ntpdc/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdc
+
+PROG= ntpdc
+NOMAN= yes
+SRCS= ntpdc.c ntpdc_ops.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP} ${LIBREADLINE} ${LIBTERMCAP}
+LDADD= ${LIBNTP} -lreadline -ltermcap
+
+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..f17aa63
--- /dev/null
+++ b/usr.sbin/ntp/ntpq/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpq
+
+BINDIR= /usr/bin
+
+PROG= ntpq
+NOMAN= yes
+SRCS= ntpq.c ntpq_ops.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP} ${LIBREADLINE} ${LIBTERMCAP}
+LDADD= ${LIBNTP} -lreadline -ltermcap
+
+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..9e14c38
--- /dev/null
+++ b/usr.sbin/ntp/ntptime/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/util
+
+PROG= ntptime
+NOMAN= yes
+SRCS= ntptime.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntptimeset/Makefile b/usr.sbin/ntp/ntptimeset/Makefile
new file mode 100644
index 0000000..240e516
--- /dev/null
+++ b/usr.sbin/ntp/ntptimeset/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdate
+
+PROG= ntptimeset
+NOMAN= yes
+SRCS= ntptimeset.c ntptime_config.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntptimeset
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntptrace/Makefile b/usr.sbin/ntp/ntptrace/Makefile
new file mode 100644
index 0000000..297339f
--- /dev/null
+++ b/usr.sbin/ntp/ntptrace/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntptrace
+
+PROG= ntptrace
+NOMAN= yes
+SRCS= ntptrace.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntptrace
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/scripts/mkver b/usr.sbin/ntp/scripts/mkver
new file mode 100755
index 0000000..a418146
--- /dev/null
+++ b/usr.sbin/ntp/scripts/mkver
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+PROG=${1-UNKNOWN}
+
+ConfStr="$PROG"
+
+ConfStr="$ConfStr 4.1.1b"
+
+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/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/ofwdump/Makefile b/usr.sbin/ofwdump/Makefile
new file mode 100644
index 0000000..b94ffd1
--- /dev/null
+++ b/usr.sbin/ofwdump/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= ofwdump
+MAN= ofwdump.8
+MANSUBDIR= /sparc64
+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..f168e97
--- /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 sparc64
+.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/pccard/Makefile b/usr.sbin/pccard/Makefile
new file mode 100644
index 0000000..10b8a4c
--- /dev/null
+++ b/usr.sbin/pccard/Makefile
@@ -0,0 +1,6 @@
+# Makefile for pccardc/pccardd.
+# $FreeBSD$
+
+SUBDIR= pccardc pccardd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pccard/Makefile.inc b/usr.sbin/pccard/Makefile.inc
new file mode 100644
index 0000000..88fdf2c
--- /dev/null
+++ b/usr.sbin/pccard/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+NOSHARED?= YES
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/pccard/pccardc/Makefile b/usr.sbin/pccard/pccardc/Makefile
new file mode 100644
index 0000000..d0649a2
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/Makefile
@@ -0,0 +1,14 @@
+# pccardc Makefile
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../pccardd
+
+PROG= pccardc
+MAN= pccardc.8
+SRCS= beep.c dumpcis.c dumpcisfile.c enabler.c pccardc.c pccardmem.c power.c \
+ printcis.c rdattr.c rdmap.c rdreg.c readcis.c wrattr.c wrreg.c
+
+CFLAGS+= -I${.CURDIR}/../pccardd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pccard/pccardc/beep.c b/usr.sbin/pccard/pccardc/beep.c
new file mode 100644
index 0000000..be62c56
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/beep.c
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+int
+beep_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, newstat, valid = 1;
+ char name[64], *p;
+
+ if (argc != 2)
+ valid = 0;
+ if (valid) {
+ for (p = argv[1]; *p; p++) {
+ if (!isdigit(*p)) {
+ valid = 0;
+ break;
+ }
+ }
+ }
+ if (!valid)
+ errx(1, "usage: %s beep newstat", argv[0]);
+
+ sscanf(argv[1], "%d", &newstat);
+ sprintf(name, CARD_DEVICE, 0);
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+ if (ioctl(fd, PIOCSBEEP, &newstat) < 0)
+ err(1, "ioctl (PIOCSBEEP)");
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/dumpcis.c b/usr.sbin/pccard/pccardc/dumpcis.c
new file mode 100644
index 0000000..d5fc757
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/dumpcis.c
@@ -0,0 +1,114 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+#include "readcis.h"
+
+int nocards;
+
+static void
+scan(slot)
+ int slot;
+{
+ int fd;
+ char name[64];
+ struct cis *cp;
+ struct slotstate st;
+
+ sprintf(name, CARD_DEVICE, slot);
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return;
+ nocards++;
+ if (ioctl(fd, PIOCGSTATE, &st))
+ err(1, "ioctl (PIOCGSTATE)");
+ if (st.state == filled) {
+ cp = readcis(fd);
+ if (cp) {
+ printf("Configuration data for card in slot %d\n",
+ slot);
+ dumpcis(cp);
+ freecis(cp);
+ }
+ }
+}
+
+void
+dump(p, sz)
+ unsigned char *p;
+ int sz;
+{
+ int ad = 0, i;
+
+ 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;
+ }
+}
+
+void *
+xmalloc(int sz)
+{
+ void *p;
+
+ sz = (sz + 7) & ~7;
+ p = malloc(sz);
+ if (p)
+ bzero(p, sz);
+ else
+ errx(1, "malloc");
+ return (p);
+}
+
+int
+dumpcis_main(int argc, char **argv)
+{
+ int node;
+
+ for (node = 0; node < 8; node++)
+ scan(node);
+ printf("%d slots found\n", nocards);
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/dumpcisfile.c b/usr.sbin/pccard/pccardc/dumpcisfile.c
new file mode 100644
index 0000000..6a415ba
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/dumpcisfile.c
@@ -0,0 +1,72 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+#include "readcis.h"
+
+static void
+scanfile(name)
+ char *name;
+{
+ int fd;
+ struct cis *cp;
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return;
+ cp = readcis(fd);
+ if (cp) {
+ printf("Configuration data for file %s\n",
+ name);
+ dumpcis(cp);
+ freecis(cp);
+ }
+ close(fd);
+}
+
+int
+dumpcisfile_main(int argc, char **argv)
+{
+
+ isdumpcisfile = 1;
+ for (argc--, argv++; argc; argc--, argv++)
+ scanfile(*argv);
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/enabler.c b/usr.sbin/pccard/pccardc/enabler.c
new file mode 100644
index 0000000..11ee0bb
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/enabler.c
@@ -0,0 +1,153 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+static void usage __P((char *));
+
+int
+enabler_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct dev_desc drv;
+ struct mem_desc mem;
+ struct io_desc io;
+ int fd, slot, i, card_addr;
+ char name[32];
+ char *p;
+
+ bzero(&drv, sizeof(drv));
+ if (argc < 3)
+ usage("arg count");
+ slot = atoi(argv[1]);
+ if (slot < 0 || slot >= MAXSLOT)
+ usage("illegal slot number");
+ p = argv[2];
+ while (*p && (*p < '0' || *p > '9'))
+ p++;
+ if (*p == 0)
+ usage("no unit on device name");
+ drv.unit = atoi(p);
+ *p = 0;
+ strcpy(drv.name, argv[2]);
+ argv += 3;
+ argc -= 3;
+ while (argc > 1) {
+ if (strcmp(argv[0], "-m") == 0) {
+ if (argc < 4)
+ usage("memory argument error");
+ if (sscanf(argv[1], "%x", &card_addr) != 1)
+ usage("bad card address");
+ if (sscanf(argv[2], "%lx", &drv.mem) != 1)
+ usage("bad memory address");
+ if (sscanf(argv[3], "%d", &i) != 1)
+ usage("bad memory size");
+ drv.memsize = i * 1024;
+ argc -= 2;
+ argv += 2;
+ } else if (strcmp(argv[0], "-f") == 0) {
+ if (sscanf(argv[1], "%x", &drv.flags) != 1)
+ usage("bad driver flags");
+ } else if (strcmp(argv[0], "-a") == 0) {
+ if (sscanf(argv[1], "%x", &drv.iobase) != 1)
+ usage("bad I/O address");
+ } else if (strcmp(argv[0], "-i") == 0) {
+ if (sscanf(argv[1], "%d", &i) != 1 || i < 1 || i > 15)
+ usage("illegal IRQ");
+ drv.irqmask = 1 << i;
+ }
+ argc -= 2;
+ argv += 2;
+ }
+ if (argc)
+ usage("no parameter for argument");
+ printf("drv %s%d, mem 0x%lx, size %d, io 0x%x, irq 0x%x, flags 0x%x\n",
+ drv.name, drv.unit, drv.mem, drv.memsize, drv.iobase,
+ drv.irqmask, drv.flags);
+ sprintf(name, CARD_DEVICE, slot);
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+
+ /* Map the memory and I/O contexts. */
+ if (drv.mem) {
+ mem.window = 0;
+ mem.flags = MDF_ACTIVE | MDF_16BITS;
+ mem.start = (caddr_t)drv.mem;
+ mem.size = drv.memsize;
+ mem.card = card_addr;
+ if (ioctl(fd, PIOCSMEM, &mem))
+ err(1, "set memory context");
+ }
+ if (drv.iobase) {
+ io.window = 0;
+ io.flags = IODF_ACTIVE | IODF_CS16;
+ io.start = drv.iobase;
+ io.size = 32; /* Blah... */
+ if (ioctl(fd, PIOCSIO, &io))
+ err(1, "set I/O context");
+ }
+ if (ioctl(fd, PIOCSDRV, &drv))
+ warn("set driver");
+ close(fd);
+ return 0;
+}
+
+/*
+ * usage - print usage and exit
+ */
+void
+usage(msg)
+ char *msg;
+{
+ fprintf(stderr, "enabler: %s\n", msg);
+ fprintf(stderr,
+"usage: pccardc enabler slot driver [-m addr size] [-a iobase] [-i irq]\n");
+ fprintf(stderr,
+" -m card addr size : card address (hex), host address (hex) & size (Kb)\n");
+ fprintf(stderr,
+" -a iobase : I/O port address (hex)\n");
+ fprintf(stderr,
+" -i irq : interrupt request number (1-15)\n");
+ fprintf(stderr,
+" Example: enabler 0 ed0 -m 2000 d4000 16 -a 300 -i 3\n");
+ exit(1);
+}
diff --git a/usr.sbin/pccard/pccardc/pccardc.8 b/usr.sbin/pccard/pccardc/pccardc.8
new file mode 100644
index 0000000..008224e
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardc.8
@@ -0,0 +1,282 @@
+.\"
+.\" Copyright (c) 1998 Toshihiko ARAI <toshi@jp.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.
+.\"
+.\" Translated to English by Hiroki Sato <hrs@geocities.co.jp>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 14, 1998
+.Dt PCCARDC 8
+.Os
+.Sh NAME
+.Nm pccardc
+.Nd PC-CARD (PCMCIA) management and monitoring tool
+.Sh SYNOPSIS
+.Nm
+.Ar subcommand
+.Op Ar arg ...
+.Sh DESCRIPTION
+The
+.Nm
+utility controls PC-CARD slots and configures and displays information
+about PCMCIA cards. It understands the following subcommands:
+.Pp
+.Bl -tag -width dumpcisfile -compact
+.It Ic beep
+Set beep type
+.It Ic dumpcis
+Print card CIS(s)
+.It Ic dumpcisfile
+Print card CIS(s) from files
+.It Ic enabler
+Device driver enabler
+.It Ic help
+Print command summary
+.It Ic pccardmem
+Allocate memory for pccard driver
+.It Ic power
+Power on/off slots and leaves them disabled
+.It Ic rdattr
+Read attribute memory
+.It Ic rdmap
+Read pcic mappings
+.It Ic rdreg
+Read pcic register
+.It Ic wrattr
+Write byte to attribute memory
+.It Ic wrreg
+Write pcic register
+.El
+.Bl -enum
+.It
+.Ic beep Ar beep_mode
+.Pp
+Specifies the sound made upon card insertion or removal.
+This subcommand corresponds to
+.Va pccard_beep
+in
+.Xr rc.conf 5 .
+Allowed values for
+.Ar beep_mode
+are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Li 0
+silent mode
+.It Li 1
+simple beep mode
+.It Li 2
+melody mode
+.El
+.It
+.Ic dumpcis
+.Op Ar slot
+.Pp
+Displays
+.Em CIS
+(Card Information Structure) tuple in EEPROM of a PC-CARD card.
+.Ar Slot
+specifies which slot to read.
+When no option is supplied, it displays
+the CIS of all of the available cards.
+.It
+.Ic dumpcisfile
+.Ar
+.Pp
+Displays CIS(s) in the same format as
+.Ic dumpcis ,
+but does so using
+one or more files
+as the input CIS data instead of physical PC-CARD cards.
+.It
+.Ic enabler Ar slot driver
+.Op Fl m Ar card addr size
+.Op Fl a Ar iobase
+.Op Fl i Ar irq
+.Pp
+This is a "point enabler" which can be used to set
+parameters manually to enable a card when
+.Xr pccardd 8
+cannot allocate drivers to a PC-CARD card properly.
+Specify a PC-CARD slot in
+.Ar slot
+and a device name (such as "ed0" or "sio2") in
+.Ar driver ,
+along with some or all of the following options:
+.Bl -tag -width Ds
+.It Fl m Ar card addr size
+maps the shared-memory window of the card to host address.
+.Ar card
+is the starting address of shared-memory (hex) in the card's address space,
+.Ar addr
+is the address (hex) to map the memory to in the computer's address space, and
+.Ar size
+is the size of memory window (kb).
+.It Fl a Ar iobase
+.Ar iobase
+specifies the port a number to be mapped to the I/O window (hex)
+.It Fl i Ar irq
+.Ar irq
+specifies the IRQ (decimal from 1 to 15) the card will use for interrupts.
+.El
+.Pp
+For example,
+.Bd -literal
+ enabler 0 ed0 -m 2000 d4000 16 -a 300 -i 3
+.Ed
+.Pp
+assigns the card in slot zero to the first NE2000 ethernet card driver at
+port 0x300 and IRQ 3,
+mapping the 16KB memory region at 0x2000 in the card to 0xd4000.
+.Pp
+This allows use of some unrecognized cards with broken CIS tuples,
+and is also useful for testing a card that has not yet reported.
+However, beware that it frequently cannot recognize new cards properly.
+.It
+.Ic help
+.Pp
+Prints help for
+.Nm .
+.It
+.Ic pccardmem Ar addr
+.Pp
+Specifies the host address using PC-CARD controller(PCIC)
+will use to map cards to.
+Because the PCIC needs a contiguous 16KB memory, and most machines
+have other ROMs or devices at other places in the ISA I/O memory
+address range, generally you can only use the following four addresses:
+.Pp
+.Bl -tag -width 0xd0000 -compact
+.It Ar 0xd0000
+0xd0000-0xd3fff (default)
+.It Ar 0xd4000
+0xd4000-0xd7fff
+.It Ar 0xd8000
+0xd8000-0xdbfff
+.It Ar 0xdc000
+0xdc000-0xdffff
+.El
+.Pp
+You can use "DEFAULT" instead of 0xd0000.
+This subcommand corresponds to
+.Va pccard_mem
+in
+.Xr rc.conf 5 .
+.It
+.Ic power Ar slot power_mode
+.Pp
+Changes the state of the power supply of the card in the specified
+.Ar slot :
+.Pp
+.Bl -tag -width Ds
+.It Li 0
+Turn off a power supply.
+If a card becomes unstable when it is removed at
+activate state,
+this can force it into inactive state first and remove it safely.
+The slot will remain disabled until you eject the card with the system
+running or reenable the card with another
+.Ic pccardc power
+power command.
+The slot will even remain disabled when you suspend and resume the
+computer.
+.It Li 1
+Turn on a power supply and set it into active state
+(the system treats the card as if it was just inserted).
+.El
+.Pp
+.It
+.Ic rdattr Ar slot offs length
+.Pp
+Prints a hex dump
+.Ar length
+bytes long of the EEPROM of the card in slot
+.Ar slot
+starting at
+.Ar offs .
+All parameters are in hex.
+.It
+.Ic rdmap
+.Op Ar slot
+.Pp
+Displays where the four memory windows and two I/O windows of a PC-CARD slot
+are mapped to on the host.
+If
+.Ar slot
+is not supplied, it displays the information for all of slots in the system.
+.It
+.Ic rdreg
+.Op Ar slot
+.Pp
+Displays the 64 registers of the card in
+.Ar slot
+(all slots by default).
+.It
+.Ic wrattr Ar slot offs value
+.Pp
+Writes a single byte to the card's EEPROM at
+an offset address from the top specified in
+.Ar offs
+(hex),
+with a value specified in
+.Ar value
+(hex).
+This is preserved after the card is removed.
+.It
+.Ic wrreg Ar slot reg value
+.Pp
+Writes a register of a PC-CARD.
+Specify a PC-CARD slot number in
+.Ar slot ,
+a register number in
+.Ar reg
+(hex) and
+a value in
+.Ar value
+(hex).
+.El
+.Sh FILES
+.Bl -tag -width /etc/rc.conf -compact
+.It Pa /etc/rc.conf
+configuration file
+.El
+.Sh SEE ALSO
+.Xr rc.conf 5 ,
+.Xr pccardd 8
+.Sh AUTHORS
+.An -nosplit
+The original version was written by
+.An Andrew McRae Aq andrew@mega.com.au .
+.An Tatsumi Hosokawa Aq hosokawa@mt.cs.keio.ac.jp
+fixed bugs and added some features.
+This man page was written by
+.An Toshihiko ARAI Aq toshi@jp.FreeBSD.org .
+.Sh BUGS
+Be careful when using
+.Ic enabler
+and
+.Ic wrattr .
+Misuse can make the system unstable or damage the card.
diff --git a/usr.sbin/pccard/pccardc/pccardc.c b/usr.sbin/pccard/pccardc/pccardc.c
new file mode 100644
index 0000000..c1f6ece
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardc.c
@@ -0,0 +1,100 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef int (*main_t)(int, char **);
+
+#define DECL(foo) int foo(int, char**)
+DECL(beep_main);
+DECL(dumpcis_main);
+DECL(dumpcisfile_main);
+DECL(enabler_main);
+DECL(help_main);
+DECL(pccardmem_main);
+DECL(power_main);
+DECL(rdattr_main);
+DECL(rdmap_main);
+DECL(rdreg_main);
+DECL(wrattr_main);
+DECL(wrreg_main);
+
+struct {
+ char *name;
+ main_t func;
+ char *help;
+} subcommands[] = {
+ { "beep", beep_main, "Beep type" },
+ { "dumpcis", dumpcis_main, "Prints CIS for all cards" },
+ { "dumpcisfile", dumpcisfile_main, "Prints CIS from a file" },
+ { "enabler", enabler_main, "Device driver enabler" },
+ { "help", help_main, "Prints command summary" },
+ { "pccardmem", pccardmem_main, "Allocate memory for pccard driver" },
+ { "power", power_main, "Power on/off slots" },
+ { "rdattr", rdattr_main, "Read attribute memory" },
+ { "rdmap", rdmap_main, "Read pcic mappings" },
+ { "rdreg", rdreg_main, "Read pcic register" },
+ { "wrattr", wrattr_main, "Write byte to attribute memory" },
+ { "wrreg", wrreg_main, "Write pcic register" },
+ { 0, 0 }
+};
+
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ for (i = 0; argc > 1 && subcommands[i].name; i++) {
+ if (!strcmp(argv[1], subcommands[i].name)) {
+ argv[1] = argv[0];
+ return (*subcommands[i].func) (argc - 1, argv + 1);
+ }
+ }
+ if (argc > 1)
+ warnx("unknown subcommand");
+ return help_main(argc, argv);
+}
+
+int
+help_main(int argc, char **argv)
+{
+ int i;
+
+ fprintf(stderr, "usage: pccardc <subcommand> <arg> ...\n");
+ fprintf(stderr, "subcommands:\n");
+ for (i = 0; subcommands[i].name; i++)
+ fprintf(stderr, "\t%s\n\t\t%s\n",
+ subcommands[i].name, subcommands[i].help);
+ return 1;
+}
diff --git a/usr.sbin/pccard/pccardc/pccardmem.c b/usr.sbin/pccard/pccardc/pccardmem.c
new file mode 100644
index 0000000..0b1d176
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardmem.c
@@ -0,0 +1,72 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: pccardc pccardmem [memory-address]\n");
+ exit(1);
+}
+
+int
+pccardmem_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char name[64];
+ int addr = 0;
+ int fd;
+
+ if (argc > 2)
+ usage();
+ sprintf(name, CARD_DEVICE, 0);
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ err(1, "%s", name);
+ if (argc == 2) {
+ if (sscanf(argv[1], "%x", &addr) != 1)
+ errx(1, "arg error");
+ }
+ if (ioctl(fd, PIOCRWMEM, &addr))
+ err(1, "ioctl (PIOCRWMEM)");
+ else
+ printf("PCCARD Memory address set to 0x%x\n", addr);
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/power.c b/usr.sbin/pccard/pccardc/power.c
new file mode 100644
index 0000000..59d43a5
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/power.c
@@ -0,0 +1,82 @@
+/*
+ * 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$ */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "PAO: power.c,v 1.3 1999/02/11 05:00:54 kuriyama Exp";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+int
+power_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, i, newstat, valid = 1;
+ char name[64], *p;
+
+ if (argc != 3)
+ valid = 0;
+ for (i = 1; i <= 2; i++) {
+ if (valid) {
+ for (p = argv[i]; *p; p++) {
+ if (!isdigit(*p)) {
+ valid = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (!valid)
+ errx(1, "usage: %s power slot newstat", argv[0]);
+
+ sscanf(argv[2], "%d", &newstat);
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+ newstat = newstat ? 1 : 0;
+ if (ioctl(fd, PIOCSVIR, &newstat) < 0)
+ err(1, "ioctl (PIOCSVIR)");
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/printcis.c b/usr.sbin/pccard/pccardc/printcis.c
new file mode 100644
index 0000000..f8a53b3
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/printcis.c
@@ -0,0 +1,1107 @@
+/*
+ * 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 <pccard/cardinfo.h>
+#include <pccard/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, 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 cis *cp)
+{
+ struct tuple *tp;
+ struct tuple_list *tl;
+ int count = 0, sz, ad, i;
+ u_char *p;
+ int func = 0;
+
+ for (tl = cp->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;
+ char **expp;
+ static 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 char *vexp[] =
+ {"10uV", "100uV", "1mV", "10mV", "100mV", "1V", "10V", "100V"};
+ static char *cexp[] =
+ {"10nA", "1uA", "10uA", "100uA", "1mA", "10mA", "100mA", "1A"};
+ static 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 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 char *exp[] =
+ {"1 ns", "10 ns", "100 ns", "1 us", "10 us", "100 us",
+ "1 ms", "10 ms"};
+ static 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, 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, char *type)
+{
+ static char *un_name[] =
+ {"512b", "2Kb", "8Kb", "32Kb", "128Kb", "512Kb", "2Mb", "reserved"};
+ static char *speed[] =
+ {"No speed", "250nS", "200nS", "150nS",
+ "100nS", "Reserved", "Reserved"};
+ static 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 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 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\tUnkn 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/pccard/pccardc/rdattr.c b/usr.sbin/pccard/pccardc/rdattr.c
new file mode 100644
index 0000000..1f90c77
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdattr.c
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+int
+rdattr_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, reg, length;
+ char name[64];
+ u_char *buf;
+ int fd;
+ off_t offs;
+
+ if (argc != 4)
+ errx(1, "Usage: %s rdattr slot offs length", argv[0]);
+
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ err(1, "%s", name);
+
+ reg = MDF_ATTR;
+ if (ioctl(fd, PIOCRWFLAG, &reg))
+ err(1, "ioctl (PIOCRWFLAG)");
+
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &length) != 1)
+ errx(1, "arg error");
+
+ offs = reg;
+ if ((buf = malloc(length)) == 0)
+ errx(1, "malloc failed");
+
+ lseek(fd, offs, SEEK_SET);
+ if (read(fd, buf, length) != length)
+ err(1, "%s", name);
+
+ for (i = 0; i < length; i++) {
+ if (i % 16 == 0) {
+ printf("%04x: ", (int) offs + i);
+ }
+ printf("%02x ", buf[i]);
+ if (i % 16 == 15) {
+ printf("\n");
+ }
+ }
+ if (i % 16 != 0) {
+ printf("\n");
+ }
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/rdmap.c b/usr.sbin/pccard/pccardc/rdmap.c
new file mode 100644
index 0000000..3ac81f9
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdmap.c
@@ -0,0 +1,109 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+static void
+dump_io(fd, nio)
+ int fd, nio;
+{
+ struct io_desc io;
+ int i;
+
+ for (i = 0; i < nio; i++) {
+ io.window = i;
+ if (ioctl(fd, PIOCGIO, &io))
+ err(1, "ioctl (PIOCGIO)");
+ printf("I/O %d: flags 0x%03x port 0x%3x size %d bytes\n",
+ io.window, io.flags, io.start, io.size);
+ }
+}
+
+static void
+dump_mem(fd, nmem)
+ int fd, nmem;
+{
+ struct mem_desc mem;
+ int i;
+
+ for (i = 0; i < nmem; i++) {
+ mem.window = i;
+ if (ioctl(fd, PIOCGMEM, &mem))
+ err(1, "ioctl (PIOCGMEM)");
+ printf("Mem %d: flags 0x%03x host %p card %04lx size %d bytes\n",
+ mem.window, mem.flags, mem.start, mem.card, mem.size);
+ }
+}
+
+static void
+scan(slot)
+ int slot;
+{
+ int fd;
+ char name[64];
+ struct slotstate st;
+
+ sprintf(name, CARD_DEVICE, slot);
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return;
+ if (ioctl(fd, PIOCGSTATE, &st))
+ err(1, "ioctl (PIOCGSTATE)");
+/*
+ if (st.state == filled)
+ */
+ {
+ dump_mem(fd, st.maxmem);
+ dump_io(fd, st.maxio);
+ }
+ close(fd);
+}
+
+int
+rdmap_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int node;
+
+ for (node = 0; node < 8; node++)
+ scan(node);
+ exit(0);
+}
diff --git a/usr.sbin/pccard/pccardc/rdreg.c b/usr.sbin/pccard/pccardc/rdreg.c
new file mode 100644
index 0000000..a317148
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdreg.c
@@ -0,0 +1,82 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+void
+dumpslot(sl)
+ int sl;
+{
+ char name[64];
+ int fd;
+ struct pcic_reg r;
+
+ sprintf(name, CARD_DEVICE, sl);
+ fd = open(name, O_RDONLY);
+ if (fd < 0) {
+ warn("%s", name);
+ return;
+ }
+ printf("Registers for slot %d\n", sl);
+ for (r.reg = 0; r.reg < 0x40; r.reg++) {
+ if (ioctl(fd, PIOCGREG, &r)) {
+ err(1, "ioctl (PIOCGREG)");
+ break;
+ }
+ if ((r.reg % 16) == 0)
+ printf("%02x:", r.reg);
+ printf(" %02x", r.value);
+ if ((r.reg % 16) == 15)
+ printf("\n");
+ }
+ close(fd);
+}
+
+int
+rdreg_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc != 2) {
+ dumpslot(0);
+ dumpslot(1);
+ } else
+ dumpslot(atoi(argv[1]));
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/wrattr.c b/usr.sbin/pccard/pccardc/wrattr.c
new file mode 100644
index 0000000..cc3df8e
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrattr.c
@@ -0,0 +1,80 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: pccardc wrattr slot offs value\n");
+ exit(1);
+}
+
+int
+wrattr_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int reg, value;
+ char name[64], c;
+ int fd;
+ off_t offs;
+
+ if (argc != 4)
+ usage();
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+
+ reg = MDF_ATTR;
+ if (ioctl(fd, PIOCRWFLAG, &reg))
+ err(1, "ioctl (PIOCRWFLAG)");
+
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &value) != 1)
+ errx(1, "arg error");
+
+ offs = reg;
+ c = value;
+ lseek(fd, offs, SEEK_SET);
+ if (write(fd, &c, 1) != 1)
+ err(1, "%s", name);
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardc/wrreg.c b/usr.sbin/pccard/pccardc/wrreg.c
new file mode 100644
index 0000000..5fda2a7
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrreg.c
@@ -0,0 +1,73 @@
+/*
+ * 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 */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/cardinfo.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: pccardc wrreg slot reg value\n");
+ exit(1);
+}
+
+int
+wrreg_main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int reg, value;
+ char name[64];
+ int fd;
+ struct pcic_reg r;
+
+ if (argc != 4)
+ usage();
+ sprintf(name, CARD_DEVICE, atoi(argv[1]));
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ err(1, "%s", name);
+ if (sscanf(argv[2], "%x", &reg) != 1 ||
+ sscanf(argv[3], "%x", &value) != 1)
+ errx(1, "arg error");
+ r.reg = reg;
+ r.value = value;
+ if (ioctl(fd, PIOCSREG, &r))
+ err(1, "ioctl (PIOCSREG)");
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardd/Makefile b/usr.sbin/pccard/pccardd/Makefile
new file mode 100644
index 0000000..b7fae27
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/Makefile
@@ -0,0 +1,15 @@
+# Makefile for pccardd
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../pccardc
+
+PROG= pccardd
+MAN= pccard.conf.5 pccardd.8
+SRCS= pccardd.c cardd.c file.c util.c readcis.c printcis.c server.c
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../pccardc
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pccard/pccardd/cardd.c b/usr.sbin/pccard/pccardd/cardd.c
new file mode 100644
index 0000000..c91ecd2
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.c
@@ -0,0 +1,1025 @@
+/*
+ * 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 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <regex.h>
+#include <machine/resource.h>
+#include <sys/ioctl.h>
+#include "cardd.h"
+
+static struct card_config *assign_driver(struct slot *, struct card *);
+static int assign_io(struct slot *);
+static int setup_slot(struct slot *);
+static void card_inserted(struct slot *);
+static void card_removed(struct slot *);
+static void pr_cmd(struct cmd *);
+static void read_ether(struct slot *);
+static void read_ether_attr2(struct slot *sp);
+
+struct slot *slots;
+
+/*
+ * Dump configuration file data.
+ */
+void
+dump_config_file(void)
+{
+ struct card *cp;
+ struct card_config *confp;
+
+ for (cp = cards; cp; cp = cp->next) {
+ printf("Card manuf %s, vers %s\n", cp->manuf, cp->version);
+ printf("Configuration entries:\n");
+ for (confp = cp->config; confp; confp = confp->next) {
+ printf("\tIndex code = ");
+ switch (confp->index_type) {
+ case DEFAULT_INDEX:
+ printf("default");
+ break;
+ case AUTO_INDEX:
+ printf("auto");
+ break;
+ default:
+ printf("0x%x", confp->index);
+ break;
+ }
+ printf(", driver name = %s\n", confp->driver->name);
+ }
+ if (cp->insert) {
+ printf("Insert commands are:\n");
+ pr_cmd(cp->insert);
+ }
+ if (cp->remove) {
+ printf("Remove commands are:\n");
+ pr_cmd(cp->remove);
+ }
+ }
+ fflush(stdout);
+}
+
+static void
+pr_cmd(struct cmd *cp)
+{
+ while (cp) {
+ printf("\t%s\n", cp->line);
+ cp = cp->next;
+ }
+}
+
+/*
+ * readslots - read all the PCMCIA slots, and build
+ * a list of the slots.
+ */
+struct slot *
+readslots(void)
+{
+ char name[128];
+ int i, fd;
+ struct slot *sp;
+
+ slots = NULL;
+ for (i = 0; i < MAXSLOT; i++) {
+ sprintf(name, CARD_DEVICE, i);
+ fd = open(name, O_RDWR);
+ if (fd < 0)
+ continue;
+ sp = xmalloc(sizeof(*sp));
+ sp->fd = fd;
+ sp->name = newstr(name);
+ sp->slot = i;
+
+ /* Check to see if the controller memory has been set up. */
+ if (slots == 0) {
+ unsigned long mem = 0;
+
+ if (ioctl(fd, PIOCRWMEM, &mem))
+ logerr("ioctl (PIOCRWMEM)");
+#ifdef DEBUG
+ logmsg("mem=0x%x\n", mem);
+#endif
+ if (mem == 0) {
+ mem = alloc_memory(4 * 1024);
+ if (mem == 0)
+ die("can't allocate memory for "
+ "controller access");
+ if (ioctl(fd, PIOCRWMEM, &mem))
+ logerr("ioctl (PIOCRWMEM)");
+ }
+ }
+ sp->next = slots;
+ slots = sp;
+ slot_change(sp);
+ }
+ return (slots);
+}
+
+/*
+ * slot_change - Card status has changed.
+ * read new state and process.
+ */
+void
+slot_change(struct slot *sp)
+{
+ struct slotstate state;
+
+ if (ioctl(sp->fd, PIOCGSTATE, &state)) {
+ logerr("ioctl (PIOCGSTATE)");
+ return;
+ }
+ switch (state.state) {
+ case empty:
+ case inactive:
+ case noslot:
+ /* Debounce potentially incorrectly reported removals */
+ if (state.laststate == filled || state.laststate == suspend)
+ card_removed(sp);
+ break;
+ case filled:
+ /* KLUDGE: if we were suspended, remove card */
+ if (state.laststate == suspend)
+ card_removed(sp);
+ card_inserted(sp);
+ break;
+ case suspend:
+ /* ignored */
+ break;
+ }
+ sp->state = state.state;
+ stat_changed(sp);
+}
+
+/*
+ * card_removed - card has been removed from slot.
+ * Execute the remove commands, and clear the slot's state.
+ * Execute the device commands, then the driver commands
+ * and then the card commands. This is the reverse
+ * order to the insertion commands
+ */
+void
+card_removed(struct slot *sp)
+{
+ struct card *cp;
+ struct allocblk *sio;
+ int in_use = 0;
+
+ if (sp->config && sp->config->driver && sp->card)
+ logmsg("%s%d: %s removed.", sp->config->driver->kernel,
+ sp->config->driver->unit, sp->card->logstr);
+ if (sp->cis)
+ freecis(sp->cis);
+ if (sp->config) {
+ if (sp->config->inuse && sp->config->driver->inuse)
+ in_use = 1;
+ sp->config->inuse = 0;
+ sp->config->driver->inuse = 0;
+ }
+ if ((cp = sp->card) != 0 && in_use)
+ execute(cp->remove, sp);
+ sp->cis = 0;
+ sp->config = 0;
+ /* release io */
+ if (sp->flags & IO_ASSIGNED)
+ for (sio = &sp->io; sio; sio = sio->next)
+ if (sio->addr && sio->size)
+ bit_nset(io_avail, sio->addr, sio->addr + sio->size - 1);
+ /* release irq */
+ if (sp->flags & IRQ_ASSIGNED)
+ if (sp->irq >= 1 && sp->irq <= 15)
+ pool_irq[sp->irq] = 1;
+}
+
+/* CIS string comparison */
+
+#define REGCOMP_FLAGS (REG_EXTENDED | REG_NOSUB)
+#define REGEXEC_FLAGS (0)
+
+static int
+cis_strcmp(char *db, char *cis)
+{
+ int res, err;
+ char buf[256];
+ regex_t rx;
+ char * p;
+ size_t n;
+
+ if (!db || !cis) {
+ return -1;
+ }
+ n = strlen(db);
+ if (n > 2 && db[0] == '/' && db[n-1] == '/') {
+ /* matching by regex */
+ db++;
+ } else {
+ /* otherwise, matching by strncmp() */
+ if (n != strlen(cis)) {
+ return -1;
+ }
+
+ return strncmp(db, cis, n);
+ }
+ p = xmalloc(n);
+ strncpy(p + 1, db, n-2);
+ *p = '^';
+ db = p;
+ if ((err = regcomp(&rx, p, REGCOMP_FLAGS))) {
+ regerror(err, &rx, buf, sizeof buf);
+ logmsg("Warning: REGEX error for\"%s\" -- %s\n", p, buf);
+ regfree(&rx);
+ free(p);
+ return -1;
+ }
+ res = regexec(&rx, cis, 0, NULL, REGEXEC_FLAGS);
+ regfree(&rx);
+ free(p);
+ return res;
+}
+
+/*
+ * card_inserted - Card has been inserted;
+ * - Read the CIS
+ * - match the card type.
+ * - Match the driver and allocate a driver instance.
+ * - Allocate I/O ports, memory and IRQ.
+ * - Set up the slot.
+ * - assign the driver (if failed, then terminate).
+ * - Run the card commands.
+ * - Run the driver commands
+ * - Run the device commands
+ */
+void
+card_inserted(struct slot *sp)
+{
+ struct card *cp;
+ int err;
+
+ usleep(pccard_init_sleep);
+ sp->cis = readcis(sp->fd);
+ if (sp->cis == 0) {
+ logmsg("Error reading CIS on %s\n", sp->name);
+ return;
+ }
+#if 0
+ dumpcis(sp->cis);
+#endif
+ for (cp = cards; cp; cp = cp->next) {
+ switch (cp->deftype) {
+ case DT_VERS:
+ if (cis_strcmp(cp->manuf, sp->cis->manuf) == 0 &&
+ cis_strcmp(cp->version, sp->cis->vers) == 0) {
+ if (cp->add_info1 != NULL &&
+ cis_strcmp(cp->add_info1, sp->cis->add_info1) != 0) {
+ break;
+ }
+ if (cp->add_info2 != NULL &&
+ cis_strcmp(cp->add_info2, sp->cis->add_info2) != 0) {
+ break;
+ }
+
+ logmsg("Card \"%s\"(\"%s\") "
+ "[%s] [%s] "
+ "matched \"%s\" (\"%s\") "
+ "[%s] [%s] ",
+ sp->cis->manuf, sp->cis->vers,
+ sp->cis->add_info1, sp->cis->add_info2,
+ cp->manuf, cp->version,
+ cp->add_info1, cp->add_info2);
+ goto escape;
+ }
+ break;
+ case DT_FUNC:
+ if (cp->func_id == sp->cis->func_id1) {
+ logmsg("Card \"%s\"(\"%s\") "
+ "[%s] [%s] "
+ "has function ID %d\n",
+ sp->cis->manuf, sp->cis->vers,
+ sp->cis->add_info1, sp->cis->add_info2,
+ cp->func_id);
+ goto escape;
+ }
+ break;
+ default:
+ logmsg("Unknown deftype %d\n", cp->deftype);
+ die("cardd.c:card_inserted()");
+ }
+ }
+escape:
+ sp->card = cp;
+#if 0
+ reset_slot(sp);
+#endif
+ if (cp == 0) {
+ logmsg("No card in database for \"%s\"(\"%s\")",
+ sp->cis->manuf, sp->cis->vers);
+ return;
+ }
+ /*
+ * Copy CIS_MANUF_ID and CIS_FUNC_EXT from "struct cis"
+ * to "struct slot"
+ */
+ if (sp->cis->lan_nid && sp->cis->lan_nid[0] == sizeof(sp->eaddr)) {
+ bcopy(sp->cis->lan_nid + 1, sp->eaddr, sizeof(sp->eaddr));
+ sp->flags |= EADDR_CONFIGED;
+ } else {
+ bzero(sp->eaddr, sizeof(sp->eaddr));
+ }
+ if (sp->cis->manufacturer && sp->cis->product) {
+ sp->manufacturer = sp->cis->manufacturer;
+ sp->product = sp->cis->product;
+ sp->prodext = sp->cis->prodext; /* For xe driver */
+ } else {
+ sp->manufacturer = 0;
+ sp->product = 0;
+ sp->prodext = 0;
+ }
+ if (sp->cis->manuf)
+ strlcpy(sp->manufstr, sp->cis->manuf, sizeof(sp->manufstr));
+ if (sp->cis->vers)
+ strlcpy(sp->versstr, sp->cis->vers, sizeof(sp->versstr));
+ if (sp->cis->add_info1)
+ strlcpy(sp->cis3str, sp->cis->add_info1, sizeof(sp->cis3str));
+ if (sp->cis->add_info2)
+ strlcpy(sp->cis4str, sp->cis->add_info2, sizeof(sp->cis4str));
+
+ if (cp->ether) {
+ struct ether *e = 0;
+ e = cp->ether;
+ switch (e->type) {
+ case ETHTYPE_ATTR2:
+ read_ether_attr2(sp);
+ break;
+ default:
+ read_ether(sp);
+ break;
+ }
+ }
+ if ((sp->config = assign_driver(sp, cp)) == NULL)
+ return;
+ if ((err = assign_io(sp))) {
+ char *reason;
+
+ switch (err) {
+ case -1:
+ reason = "specified CIS was not found";
+ break;
+ case -2:
+ reason = "memory block allocation failed";
+ break;
+ case -3:
+ reason = "I/O block allocation failed";
+ break;
+ case -4:
+ reason = "requires more than one memory window";
+ break;
+ default:
+ reason = "Unknown";
+ break;
+ }
+ logmsg("Resource allocation failure for \"%s\"(\"%s\") "
+ "[%s] [%s]; Reason %s\n",
+ sp->cis->manuf, sp->cis->vers,
+ sp->cis->add_info1, sp->cis->add_info2, reason);
+ return;
+ }
+
+ /*
+ *
+ * Once assigned, set up the I/O & mem contexts, set up the
+ * windows, and then attach the driver.
+ */
+ if (setup_slot(sp))
+ execute(cp->insert, sp);
+#if 0
+ else
+ reset_slot(sp);
+#endif
+}
+
+/*
+ * read_ether - read ethernet address from card. Offset is
+ * the offset into the attribute memory of the card.
+ */
+static void
+read_ether(struct slot *sp)
+{
+ unsigned char net_addr[12];
+ int flags = MDF_ATTR; /* attribute memory */
+
+ ioctl(sp->fd, PIOCRWFLAG, &flags);
+ lseek(sp->fd, (off_t)sp->card->ether->value, SEEK_SET);
+ if (read(sp->fd, net_addr, sizeof(net_addr)) != sizeof(net_addr)) {
+ logerr("read err on net addr");
+ return;
+ }
+ sp->eaddr[0] = net_addr[0];
+ sp->eaddr[1] = net_addr[2];
+ sp->eaddr[2] = net_addr[4];
+ sp->eaddr[3] = net_addr[6];
+ sp->eaddr[4] = net_addr[8];
+ sp->eaddr[5] = net_addr[10];
+ logmsg("Ether=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ sp->eaddr[0], sp->eaddr[1], sp->eaddr[2],
+ sp->eaddr[3], sp->eaddr[4], sp->eaddr[5]);
+ sp->flags |= EADDR_CONFIGED;
+}
+
+/*
+ * Megahertz X-Jack Ethernet uses unique way to get/set MAC
+ * address of the card.
+ */
+static void
+read_ether_attr2(struct slot *sp)
+{
+ int i;
+ char *hexaddr;
+
+ hexaddr = sp->cis->add_info2;
+ for (i = 0; i < 6; i++)
+ sp->eaddr[i] = 0;
+ if (!hexaddr)
+ return;
+ if (strlen(hexaddr) != 12)
+ return;
+ for (i = 0; i < 12; i++)
+ if (!isxdigit(hexaddr[i]))
+ return;
+ for (i = 0; i < 6; i++) {
+ u_int d;
+ char s[3];
+ s[0] = hexaddr[i * 2];
+ s[1] = hexaddr[i * 2 + 1];
+ s[2] = '\0';
+ if (!sscanf(s, "%x", &d)) {
+ int j;
+ for (j = 0; j < 6; j++)
+ sp->eaddr[j] = 0;
+ return;
+ }
+ sp->eaddr[i] = (u_char)d;
+ }
+ sp->flags |= EADDR_CONFIGED;
+}
+
+
+/*
+ * assign_driver - Assign driver to card.
+ * First, see if an existing driver is already setup.
+ */
+static struct card_config *
+assign_driver(struct slot *sp, struct card *cp)
+{
+ struct driver *drvp;
+ struct card_config *conf;
+ struct pccard_resource res;
+ int i;
+ int irqmin, irqmax;
+
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->card == cp &&
+ conf->driver->config == conf &&
+ conf->driver->inuse == 0) {
+ if (debug_level > 0) {
+ logmsg("Found existing driver (%s) for %s\n",
+ conf->driver->name, cp->manuf);
+ }
+ conf->driver->inuse = 1;
+ conf->inuse = 1;
+ return (conf);
+ }
+ /*
+ * New driver must be allocated. Find the first configuration
+ * not in use.
+ */
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->inuse/*card*/ == 0)
+ break;
+ if (conf == 0) {
+ logmsg("No free configuration for card %s", cp->manuf);
+ return (NULL);
+ }
+ /*
+ * Now we have a free driver and a matching configuration.
+ * Before assigning and allocating everything, check to
+ * see if a device class can be allocated to this.
+ */
+ drvp = conf->driver;
+
+ /* If none available, then we can't use this card. */
+ if (drvp->inuse) {
+ logmsg("Driver already being used for %s", cp->manuf);
+ return (NULL);
+ }
+
+ /*
+ * Allocate a free IRQ if none has been specified. When we're
+ * sharing interrupts (cardbus bridge case), then we'll use what
+ * the kernel tells us to use, reguardless of what the user
+ * configured. Asking the kernel for IRQ 0 is our way of asking
+ * if we should use a shared interrupt.
+ */
+ res.type = SYS_RES_IRQ;
+ res.size = 1;
+ if (conf->irq == 0) {
+ irqmin = 1;
+ irqmax = 15;
+ } else {
+ irqmin = irqmax = conf->irq;
+ conf->irq = 0; /* Make sure we get it. */
+ }
+ res.min = 0;
+ res.max = 0;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr != ~0ul) {
+ conf->irq = res.resource_addr;
+ pool_irq[conf->irq] = 0;
+ } else {
+ for (i = irqmin; i <= irqmax; i++) {
+ /*
+ * Skip irqs not in the pool.
+ */
+ if (pool_irq[i] == 0)
+ continue;
+ /*
+ * -I forces us to use the interrupt, so use it.
+ */
+ if (!use_kern_irq) {
+ conf->irq = i;
+ pool_irq[i] = 0;
+ break;
+ }
+ /*
+ * Ask the kernel if we have a free irq.
+ */
+ res.min = i;
+ res.max = i;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr == ~0ul)
+ continue;
+ /*
+ * res.resource_addr might be the kernel's
+ * better idea than i, so we have to check to
+ * see if that's in use too. If not, mark it
+ * in use and break out of the loop. I'm not
+ * sure this can happen when IRQ 0 above fails,
+ * but the test is cheap enough.
+ */
+ if (pool_irq[res.resource_addr] == 0)
+ continue;
+ conf->irq = res.resource_addr;
+ pool_irq[conf->irq] = 0;
+ break;
+ }
+ }
+ if (conf->irq == 0) {
+ logmsg("Failed to allocate IRQ for %s\n", cp->manuf);
+ return (NULL);
+ }
+ drvp->card = cp;
+ drvp->config = conf;
+ drvp->inuse = 1;
+ conf->inuse = 1;
+ return (conf);
+}
+
+/*
+ * Auto select config index
+ */
+static struct cis_config *
+assign_card_index(struct slot *sp, struct cis * cis)
+{
+ struct cis_config *cp;
+ struct cis_ioblk *cio;
+ struct pccard_resource res;
+ int i;
+
+ res.type = SYS_RES_IOPORT;
+ for (cp = cis->conf; cp; cp = cp->next) {
+ if (!cp->iospace || !cp->io)
+ continue;
+ for (cio = cp->io; cio; cio = cio->next) {
+ res.size = cio->size;
+ res.min = cio->addr;
+ res.max = res.min + cio->size - 1;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr != cio->addr)
+ goto next;
+ for (i = cio->addr; i < cio->addr + cio->size - 1; i++)
+ if (!bit_test(io_avail, i))
+ goto next;
+ }
+ return cp; /* found */
+ next:;
+ }
+ return cis->def_config;
+}
+
+/*
+ * assign_io - Allocate resources to slot matching the
+ * configuration index selected.
+ */
+static int
+assign_io(struct slot *sp)
+{
+ struct cis *cis;
+ struct cis_config *cisconf, *defconf;
+
+ cis = sp->cis;
+ defconf = cis->def_config;
+ switch (sp->config->index_type) {
+ case DEFAULT_INDEX: /* default */
+ cisconf = defconf;
+ sp->config->index = cisconf->id;
+ break;
+ case AUTO_INDEX: /* auto */
+ cisconf = assign_card_index(sp, cis);
+ if (cisconf)
+ sp->config->index = cisconf->id;
+ break;
+ default: /* normal, use index value */
+ for (cisconf = cis->conf; cisconf; cisconf = cisconf->next)
+ if (cisconf->id == sp->config->index)
+ break;
+ }
+
+ if (cisconf == 0) {
+ logmsg("Config id %d not present in this card",
+ sp->config->index);
+ return (-1);
+ }
+ sp->card_config = cisconf;
+
+ /*
+ * Found a matching configuration. Now look at the I/O, memory and IRQ
+ * to create the desired parameters. Look at memory first.
+ */
+
+ /* Skip ed cards in PIO mode */
+ if ((strncmp(sp->config->driver->name, "ed", 2) == 0) &&
+ (sp->config->flags & 0x10))
+ goto memskip;
+
+ if (cisconf->memspace || (defconf && defconf->memspace)) {
+ struct cis_memblk *mp;
+
+ mp = cisconf->mem;
+ /*
+ * Currently we do not handle the presence of multiple windows.
+ * Then again neither does the interface to the kernel!
+ * See setup_slot() and readcis.c:cis_conf()
+ */
+ if (cisconf->memwins > 1) {
+ logmsg("Card requires %d memory windows.",
+ cisconf->memwins);
+ return (-4);
+ }
+
+ if (!cisconf->memspace)
+ mp = defconf->mem;
+ sp->mem.size = mp->length;
+ sp->mem.cardaddr = mp->address;
+
+ /* For now, we allocate our own memory from the pool. */
+ sp->mem.addr = sp->config->driver->mem;
+ /*
+ * Host memory address is required. Allocate one
+ * from our pool.
+ */
+ if (sp->mem.size && sp->mem.addr == 0) {
+ sp->mem.addr = alloc_memory(mp->length);
+ if (sp->mem.addr == 0)
+ return (-2);
+ sp->config->driver->mem = sp->mem.addr;
+ }
+ /* Driver specific set up */
+ if (strncmp(sp->config->driver->name, "ed", 2) == 0) {
+ sp->mem.cardaddr = 0x4000;
+ sp->mem.flags = MDF_ACTIVE | MDF_16BITS;
+ } else {
+ sp->mem.flags = MDF_ACTIVE | MDF_16BITS;
+ }
+
+ if (sp->mem.flags & MDF_ACTIVE)
+ sp->flags |= MEM_ASSIGNED;
+ if (debug_level > 0) {
+ logmsg("Using mem addr 0x%x, size %d, card addr 0x%x, flags 0x%x\n",
+ sp->mem.addr, sp->mem.size, sp->mem.cardaddr,
+ sp->mem.flags);
+ }
+ }
+memskip:
+
+ /* Now look at I/O. */
+ bzero(&sp->io, sizeof(sp->io));
+ if (cisconf->iospace || (defconf && defconf->iospace)
+ || sp->card->iosize) {
+ struct cis_config *cp;
+ struct cis_ioblk *cio;
+ struct allocblk *sio;
+ int x, xmax;
+ int iosize;
+
+ cp = cisconf;
+ if (!cisconf->iospace)
+ cp = defconf;
+ iosize = sp->card->iosize;
+
+ /* iosize auto */
+ if (iosize < 0) {
+ if (cp->io)
+ iosize = cp->io->size;
+ else
+ iosize = 1 << cp->io_addr;
+ }
+
+ /*
+ * If # of I/O lines decoded == 10, then card does its
+ * own decoding.
+ *
+ * If an I/O block exists, then use it.
+ * If no address (but a length) is available, allocate
+ * from the pool.
+ */
+ cio = cp->io;
+ sio = &(sp->io);
+ xmax = 1;
+ if (iosize == 0 && cio)
+ xmax = cisconf->io_blks;
+ for (x = 0; x < xmax; x++) {
+ if (iosize) {
+ sio->addr = 0;
+ sio->size = iosize;
+ } else if (cio) {
+ sio->addr = cio->addr;
+ sio->size = cio->size;
+ } else {
+ /*
+ * No I/O block, assume the address lines
+ * decode gives the size.
+ */
+ sio->size = 1 << cp->io_addr;
+ }
+ if (sio->addr == 0) {
+ struct pccard_resource res;
+ int i, j;
+
+ res.type = SYS_RES_IOPORT;
+ res.size = sio->size;
+
+ for (i = 0; i < IOPORTS; i++) {
+ j = bit_fns(io_avail, IOPORTS, i,
+ sio->size, sio->size);
+ if ((j & (sio->size - 1)) != 0)
+ continue;
+ res.min = j;
+ res.max = j + sio->size - 1;
+ if (ioctl(sp->fd, PIOCSRESOURCE, &res) < 0)
+ err(1, "ioctl (PIOCSRESOURCE)");
+ if (res.resource_addr == j)
+ break;
+ }
+ if (j < 0) {
+ return (-3);
+ } else {
+ sio->addr = j;
+ }
+ }
+ bit_nclear(io_avail, sio->addr,
+ sio->addr + sio->size - 1);
+ sp->flags |= IO_ASSIGNED;
+
+ /* Set up the size to take into account the decode lines. */
+ sio->cardaddr = cp->io_addr;
+ switch (cp->io_bus) {
+ case 0:
+ break;
+ case 1:
+ sio->flags = IODF_WS;
+ break;
+ case 2:
+ sio->flags = IODF_WS | IODF_CS16;
+ break;
+ case 3:
+ sio->flags = IODF_WS | IODF_CS16 | IODF_16BIT;
+ break;
+ }
+ if (debug_level > 0) {
+ logmsg("Using I/O addr 0x%x, size %d\n",
+ sio->addr, sio->size);
+ }
+ if (cio && cio->next) {
+ sio->next = xmalloc(sizeof(*sio));
+ sio = sio->next;
+ cio = cio->next;
+ }
+ }
+ }
+ sp->irq = sp->config->irq;
+ sp->flags |= IRQ_ASSIGNED;
+ return (0);
+}
+
+/*
+ * setup_slot - Allocate the I/O and memory contexts
+ * return true if completed OK.
+ */
+static int
+setup_slot(struct slot *sp)
+{
+ struct mem_desc mem;
+ struct io_desc io;
+ struct dev_desc drv;
+ struct driver *drvp = sp->config->driver;
+ struct allocblk *sio;
+ char *p;
+ char c;
+ off_t offs;
+ int rw_flags;
+ int iowin;
+
+ memset(&io, 0, sizeof io);
+ memset(&drv, 0, sizeof drv);
+ offs = sp->cis->reg_addr;
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+#if RESET_MAY_BE_HARMFUL
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time * 1000);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x00;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time * 1000);
+#endif
+ lseek(sp->fd, offs, SEEK_SET);
+ c = sp->config->index;
+ c |= 0x40;
+ write(sp->fd, &c, sizeof(c));
+ if (debug_level > 0) {
+ logmsg("Setting config reg at offs 0x%lx to 0x%x, "
+ "Reset time = %d ms\n", (unsigned long)offs, c,
+ sp->card->reset_time);
+ }
+ usleep(pccard_init_sleep);
+ usleep(sp->card->reset_time * 1000);
+
+ /* If other config registers exist, set them up. */
+ if (sp->cis->ccrs & 2) {
+ /* CCSR */
+ c = 0;
+ if (sp->cis->def_config && sp->cis->def_config->misc_valid &&
+ (sp->cis->def_config->misc & 0x8))
+ c |= 0x08;
+ if (sp->card_config->io_bus == 1)
+ c |= 0x20;
+ lseek(sp->fd, offs + 2, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+ }
+ if (sp->flags & MEM_ASSIGNED) {
+ mem.window = 0;
+ mem.flags = sp->mem.flags;
+ mem.start = (caddr_t) sp->mem.addr;
+ mem.card = sp->mem.cardaddr;
+ mem.size = sp->mem.size;
+ if (ioctl(sp->fd, PIOCSMEM, &mem)) {
+ logerr("ioctl (PIOCSMEM)");
+ return (0);
+ }
+ }
+ if (sp->flags & IO_ASSIGNED) {
+ for (iowin = 0, sio = &(sp->io); iowin <= 1; iowin++) {
+ io.window = iowin;
+ if (sio->size) {
+ io.flags = sio->flags;
+ io.start = sio->addr;
+ io.size = sio->size;
+ }
+#if 0
+ io.start = sp->io.addr & ~((1 << sp->io.cardaddr) - 1);
+ io.size = 1 << sp->io.cardaddr;
+ if (io.start < 0x100) {
+ io.start = 0x100;
+ io.size = 0x300;
+ }
+#endif
+ if (debug_level > 0) {
+ logmsg("Assigning I/O window %d, start 0x%x, "
+ "size 0x%x flags 0x%x\n", io.window, io.start,
+ io.size, io.flags);
+ }
+ io.flags |= IODF_ACTIVE;
+ if (ioctl(sp->fd, PIOCSIO, &io)) {
+ logerr("ioctl (PIOCSIO)");
+ return (0);
+ }
+ if (ioctl(sp->fd, PIOCGIO, &io))
+ {
+ logerr("ioctl (PIOCGIO)");
+ return(0);
+ }
+ if (io.start != sio->addr){
+ logmsg("I/O base address changed from 0x%x to 0x%x\n",
+ sio->addr, io.start);
+ sio->addr = io.start;
+ }
+ if (sio->next)
+ sio = sio->next;
+ else
+ break;
+ }
+ }
+ strcpy(drv.name, drvp->kernel);
+ drv.unit = drvp->unit;
+ drv.irqmask = 1 << sp->irq;
+ drv.flags = sp->config->flags;
+ if (sp->flags & MEM_ASSIGNED) {
+ drv.mem = sp->mem.addr;
+ drv.memsize = sp->mem.size;
+ } else {
+ drv.mem = 0;
+ drv.memsize = 0;
+ }
+ if (sp->flags & IO_ASSIGNED)
+ drv.iobase = sp->io.addr;
+ else
+ drv.iobase = 0;
+ drv.iosize = sp->io.size;
+ if (debug_level > 0) {
+ logmsg("Assign %s%d, io 0x%x-0x%x, mem 0x%lx, %d bytes, "
+ "irq %d, flags %x\n", drv.name, drv.unit, drv.iobase,
+ drv.iobase + sp->io.size - 1, drv.mem, drv.memsize,
+ sp->irq, drv.flags);
+ }
+ /*
+ * Copy CIS_MANUF_ID from "struct slot" to "struct dev_desc"
+ * This means moving CIS_MANUF_ID to kernel driver area.
+ */
+ drv.manufacturer = sp->manufacturer;
+ drv.product = sp->product;
+ drv.prodext = sp->prodext;
+ strlcpy(drv.manufstr, sp->manufstr, sizeof(drv.manufstr));
+ strlcpy(drv.versstr, sp->versstr, sizeof(drv.versstr));
+ strlcpy(drv.cis3str, sp->cis3str, sizeof(drv.cis3str));
+ strlcpy(drv.cis4str, sp->cis4str, sizeof(drv.cis4str));
+ /*
+ * If the driver fails to be connected to the device,
+ * then it may mean that the driver did not recognise it.
+ */
+ memcpy(drv.misc, sp->eaddr, 6);
+ if (ioctl(sp->fd, PIOCSDRV, &drv)) {
+ logmsg("driver allocation failed for %s(%s): %s",
+ sp->card->manuf, sp->card->version, strerror(errno));
+ return (0);
+ }
+ drv.name[sizeof(drv.name) - 1] = '\0';
+ if (strncmp(drv.name, drvp->kernel, sizeof(drv.name))) {
+ drvp->kernel = newstr(drv.name);
+ p = drvp->kernel;
+ while (*p++)
+ if (*p >= '0' && *p <= '9') {
+ drvp->unit = atoi(p);
+ *p = '\0';
+ break;
+ }
+ }
+ logmsg("%s%d: %s inserted.", sp->config->driver->kernel,
+ sp->config->driver->unit, sp->card->logstr);
+ return (1);
+}
diff --git a/usr.sbin/pccard/pccardd/cardd.h b/usr.sbin/pccard/pccardd/cardd.h
new file mode 100644
index 0000000..ab7a0e1
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.h
@@ -0,0 +1,211 @@
+/*
+ * 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$
+ *
+ * Common include file for PCMCIA daemon
+ */
+#include <bitstring.h>
+
+#include <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+struct cmd {
+ struct cmd *next;
+ char *line; /* Command line */
+ int macro; /* Contains macros */
+};
+
+struct card_config {
+ struct card_config *next;
+ unsigned char index_type;
+ unsigned char index;
+ struct driver *driver;
+ int irq;
+ int flags;
+ char inuse;
+};
+
+struct ether {
+ struct ether *next;
+ int type;
+ int value;
+};
+
+#define ETHTYPE_GENERIC 0
+#define ETHTYPE_ATTR2 1
+
+struct card {
+ struct card *next;
+ char *manuf;
+ char *version;
+ char *add_info1;
+ char *add_info2;
+ u_char func_id;
+ int deftype;
+ struct ether *ether; /* For net cards, ether at offset */
+ int reset_time; /* Reset time */
+ int iosize; /* I/O window size (ignore location) */
+ struct card_config *config; /* List of configs */
+ struct cmd *insert; /* Insert commands */
+ struct cmd *remove; /* Remove commands */
+ char *logstr; /* String for logger */
+};
+
+struct driver {
+ struct driver *next;
+ char *name;
+ char *kernel; /* Kernel driver base name */
+ int unit; /* Unit of driver */
+ /*
+ * The rest of the structure is allocated dynamically.
+ * Once allocated, it stays allocated.
+ */
+ struct card *card; /* Current card, if any */
+ struct card_config *config; /* Config back ptr */
+ unsigned int mem; /* Allocated host address (if any) */
+ int inuse;
+};
+
+/*
+ * Defines one allocation block i.e a starting address
+ * and size. Used for either memory or I/O ports
+ */
+struct allocblk {
+ struct allocblk *next;
+ int addr; /* Address */
+ int size; /* Size */
+ int flags; /* Flags for block */
+ int cardaddr; /* Card address */
+};
+/*
+ * Slot structure - data held for each slot.
+ */
+struct slot {
+ struct slot *next;
+ int fd;
+ int mask;
+ int slot;
+ char *name;
+ enum cardstate state;
+ struct cis *cis;
+ struct card *card; /* Current card */
+ struct card_config *config; /* Current configuration */
+ struct cis_config *card_config;
+ char devname[16];
+ u_int manufacturer;
+ u_int product;
+ u_int prodext;
+ unsigned char eaddr[6]; /* If any */
+ char manufstr[DEV_MAX_CIS_LEN];
+ char versstr[DEV_MAX_CIS_LEN];
+ char cis3str[DEV_MAX_CIS_LEN];
+ char cis4str[DEV_MAX_CIS_LEN];
+ struct allocblk io; /* I/O block spec */
+ struct allocblk mem; /* Memory block spec */
+ int irq; /* Irq value */
+ int flags; /* Resource assignment flags */
+};
+
+/*
+ * Slot resource assignment/configuration flags
+ */
+#define IO_ASSIGNED 0x1
+#define MEM_ASSIGNED 0x2
+#define IRQ_ASSIGNED 0x4
+#define EADDR_CONFIGED 0x8
+#define WL_CONFIGED 0x10
+#define AFLAGS (IO_ASSIGNED | MEM_ASSIGNED | IRQ_ASSIGNED)
+#define CFLAGS (EADDR_CONFIGED | WL_CONFIGED)
+
+EXTERN struct slot *slots, *current_slot;
+EXTERN int slen;
+
+EXTERN struct allocblk *pool_ioblks; /* I/O blocks in the pool */
+EXTERN struct allocblk *pool_mem; /* Memory in the pool */
+EXTERN int pool_irq[16]; /* IRQ allocations */
+EXTERN int irq_init[16]; /* initial IRQ allocations */
+EXTERN struct driver *drivers; /* List of drivers */
+EXTERN struct card *cards;
+EXTERN struct card *last_card;
+EXTERN bitstr_t *mem_avail;
+EXTERN bitstr_t *mem_init;
+EXTERN bitstr_t *io_avail;
+EXTERN bitstr_t *io_init;
+EXTERN int pccard_init_sleep; /* Time to sleep on init */
+EXTERN int use_kern_irq;
+EXTERN int debug_level;
+
+/* cardd.c functions */
+void dump_config_file(void);
+struct slot *readslots(void);
+void slot_change(struct slot *);
+
+/* util.c functions */
+unsigned long alloc_memory(int);
+int bit_fns(bitstr_t *, int, int, int, int);
+void die(char *);
+void execute(struct cmd *, struct slot *);
+void logmsg(const char *, ...);
+void log_setup(void);
+void logerr(char *);
+char *newstr();
+void reset_slot(struct slot *);
+void *xmalloc(int);
+
+/* file.c functions */
+void readfile(char *);
+
+/* server.c functions */
+void set_socket(int);
+void stat_changed(struct slot *);
+void process_client(void);
+
+#define IOPORTS 0x1000 /* allow most of the low ports */
+#define MEMUNIT 0x1000
+#define MEMSTART 0xA0000
+#define MEMEND 0x100000
+#define MEMBLKS ((MEMEND-MEMSTART)/MEMUNIT)
+#define MEM2BIT(x) (((x)-MEMSTART)/MEMUNIT)
+#define BIT2MEM(x) (((x)*MEMUNIT)+MEMSTART)
+
+#define MAXINCLUDES 10
+#define MAXERRORS 10
+
+/*
+ * Config index types
+ */
+#define NORMAL_INDEX 0
+#define DEFAULT_INDEX 1
+#define AUTO_INDEX 2
+
+#define DT_VERS 0
+#define DT_FUNC 1
diff --git a/usr.sbin/pccard/pccardd/file.c b/usr.sbin/pccard/pccardd/file.c
new file mode 100644
index 0000000..93ad066
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/file.c
@@ -0,0 +1,1096 @@
+/*
+ * 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 */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "cardd.h"
+
+static FILE *in;
+static int includes = 0;
+static struct {
+ FILE *filep;
+ char *filename;
+ int lineno;
+} configfiles[MAXINCLUDES] = {{NULL, NULL, 0}, };
+
+static int pushc, pusht;
+static int lineno;
+static char *filename;
+
+static char *keys[] = {
+ "__EOF__", /* 1 */
+ "io", /* 2 */
+ "irq", /* 3 */
+ "memory", /* 4 */
+ "card", /* 5 */
+ "device", /* 6 */
+ "config", /* 7 */
+ "reset", /* 8 */
+ "ether", /* 9 */
+ "insert", /* 10 */
+ "remove", /* 11 */
+ "iosize", /* 12 */
+ "debuglevel", /* 13 */
+ "include", /* 14 */
+ "function", /* 15 */
+ "logstr", /* 16 */
+ 0
+};
+
+#define KWD_EOF 1
+#define KWD_IO 2
+#define KWD_IRQ 3
+#define KWD_MEMORY 4
+#define KWD_CARD 5
+#define KWD_DEVICE 6
+#define KWD_CONFIG 7
+#define KWD_RESET 8
+#define KWD_ETHER 9
+#define KWD_INSERT 10
+#define KWD_REMOVE 11
+#define KWD_IOSIZE 12
+#define KWD_DEBUGLEVEL 13
+#define KWD_INCLUDE 14
+#define KWD_FUNCTION 15
+#define KWD_LOGSTR 16
+
+/* for keyword compatibility with PAO/plain FreeBSD */
+static struct {
+ char *alias;
+ u_int key;
+} key_aliases[] = {
+ {"generic", KWD_FUNCTION},
+ {0, 0}
+};
+
+struct flags {
+ char *name;
+ int mask;
+};
+
+extern int doverbose;
+
+static void parsefile(void);
+static char *getline(void);
+static char *next_tok(void);
+static int num_tok(void);
+static void error(char *);
+static int keyword(char *);
+static int irq_tok(int);
+static int config_tok(unsigned char *);
+static int func_tok(void);
+static int debuglevel_tok(int);
+static struct allocblk *ioblk_tok(int);
+static struct allocblk *memblk_tok(int);
+static struct driver *new_driver(char *);
+static int iosize_tok(void);
+static void file_include(char *);
+
+static void addcmd(struct cmd **);
+static void parse_card(int);
+
+static void
+delete_card(struct card *cp)
+{
+ struct ether *etherp, *ether_next;
+ struct card_config *configp, *config_next;
+ struct cmd *cmdp, *cmd_next;
+
+ /* free strings */
+ free(cp->manuf);
+ free(cp->version);
+ free(cp->add_info1);
+ free(cp->add_info2);
+ free(cp->logstr);
+
+ /* free structures */
+ for (etherp = cp->ether; etherp; etherp = ether_next) {
+ ether_next = etherp->next;
+ free(etherp);
+ }
+ for (configp = cp->config; configp; configp = config_next) {
+ config_next = configp->next;
+ free(configp);
+ }
+ for (cmdp = cp->insert; cmdp; cmdp = cmd_next) {
+ cmd_next = cmdp->next;
+ free(cmdp->line);
+ free(cmdp);
+ }
+ for (cmdp = cp->remove; cmdp; cmdp = cmd_next) {
+ cmd_next = cmdp->next;
+ free(cmdp->line);
+ free(cmdp);
+ }
+ free(cp);
+}
+
+/*
+ * Read a file and parse the pcmcia configuration data.
+ * After parsing, verify the links.
+ */
+void
+readfile(char *name)
+{
+ int i, inuse;
+ struct card *cp, *card_next;
+ struct card *genericp, *tail_gp;
+ struct card_config *configp;
+
+ /* delete all card configuration data before we proceed */
+ genericp = 0;
+ cp = cards;
+ cards = last_card = 0;
+ while (cp) {
+ card_next = cp->next;
+
+ /* check whether this card is in use */
+ inuse = 0;
+ for (configp = cp->config; configp; configp = configp->next) {
+ if (configp->inuse) {
+ inuse = 1;
+ break;
+ }
+ }
+
+ /*
+ * don't delete entry in use for consistency.
+ * leave normal entry in the cards list,
+ * insert generic entry into the list after re-loading config files.
+ */
+ if (inuse == 1) {
+ cp->next = 0; /* unchain from the cards list */
+ switch (cp->deftype) {
+ case DT_VERS:
+ /* don't delete this entry for consistency */
+ if (debug_level >= 1) {
+ logmsg("Card \"%s\"(\"%s\") is in use, "
+ "can't change configuration\n",
+ cp->manuf, cp->version);
+ }
+ /* add this to the card list */
+ if (!last_card) {
+ cards = last_card = cp;
+ } else {
+ last_card->next = cp;
+ last_card = cp;
+ }
+ break;
+
+ case DT_FUNC:
+ /* generic entry must be inserted to the list later */
+ if (debug_level >= 1) {
+ logmsg("Generic entry is in use, "
+ "can't change configuration\n");
+ }
+ cp->next = genericp;
+ genericp = cp;
+ break;
+ }
+ } else {
+ delete_card(cp);
+ }
+
+ cp = card_next;
+ }
+
+ for (i = 0; i < MAXINCLUDES; i++) {
+ if (configfiles[i].filep) {
+ fclose(configfiles[i].filep);
+ configfiles[i].filep = NULL;
+ if (i > 0) {
+ free(configfiles[i].filename);
+ }
+ }
+ }
+ in = fopen(name, "r");
+ if (in == 0) {
+ logerr(name);
+ die("readfile");
+ }
+ includes = 0;
+ configfiles[includes].filep = in;
+ filename = configfiles[includes].filename = name;
+
+ parsefile();
+ for (cp = cards; cp; cp = cp->next) {
+ if (cp->config == 0)
+ logmsg("warning: card %s(%s) has no valid configuration\n",
+ cp->manuf, cp->version);
+ }
+
+ /* insert generic entries in use into the top of generic entries */
+ if (genericp) {
+ /* search tail of generic entries in use */
+ for (tail_gp = genericp; tail_gp->next; tail_gp = tail_gp->next)
+ ;
+
+ /*
+ * if the top of cards list is generic entry,
+ * insert generic entries in use before it.
+ */
+ if (cards && cards->deftype == DT_FUNC) {
+ tail_gp->next = cards;
+ cards = genericp;
+ goto generic_done;
+ }
+
+ /* search top of generic entries */
+ for (cp = cards; cp; cp = cp->next) {
+ if (cp->next && cp->next->deftype == DT_FUNC) {
+ break;
+ }
+ }
+
+ /*
+ * if we have generic entry in the cards list,
+ * insert generic entries in use into there.
+ */
+ if (cp) {
+ tail_gp->next = cp->next;
+ cp->next = genericp;
+ goto generic_done;
+ }
+
+ /*
+ * otherwise we don't have generic entries in
+ * cards list, just add them to the list.
+ */
+ if (!last_card) {
+ cards = genericp;
+ } else {
+ last_card->next = genericp;
+ last_card = tail_gp;
+ }
+generic_done:;
+ }
+
+ /* save the initial state of resource pool */
+ bcopy(io_avail, io_init, bitstr_size(IOPORTS));
+ bcopy(mem_avail, mem_init, bitstr_size(MEMBLKS));
+ bcopy(pool_irq, irq_init, sizeof(pool_irq));
+}
+
+static void
+parsefile(void)
+{
+ int i;
+ int errors = 0;
+ struct allocblk *bp, *next;
+ char *incl;
+
+ pushc = 0;
+ lineno = 1;
+ for (;;)
+ switch (keyword(next_tok())) {
+ case KWD_EOF:
+ /* EOF */
+ return;
+ case KWD_IO:
+ /* override reserved I/O blocks */
+ bit_nclear(io_avail, 0, IOPORTS-1);
+ for (bp = pool_ioblks; bp; bp = next) {
+ next = bp->next;
+ free(bp);
+ }
+ pool_ioblks = NULL;
+
+ while ((bp = ioblk_tok(0)) != 0) {
+ if (bp->size == 0 || bp->addr == 0) {
+ free(bp);
+ continue;
+ }
+ bit_nset(io_avail, bp->addr,
+ bp->addr + bp->size - 1);
+ bp->next = pool_ioblks;
+ pool_ioblks = bp;
+ }
+ pusht = 1;
+ break;
+ case KWD_IRQ:
+ /* override reserved irqs */
+ bzero(pool_irq, sizeof(pool_irq));
+ while ((i = irq_tok(0)) > 0)
+ pool_irq[i] = 1;
+ pusht = 1;
+ break;
+ case KWD_MEMORY:
+ /* override reserved memory blocks. */
+ bit_nclear(mem_avail, 0, MEMBLKS-1);
+ for (bp = pool_mem; bp; bp = next) {
+ next = bp->next;
+ free(bp);
+ }
+ pool_mem = NULL;
+
+ while ((bp = memblk_tok(0)) != 0) {
+ if (bp->size == 0 || bp->addr == 0) {
+ free(bp);
+ continue;
+ }
+ bit_nset(mem_avail, MEM2BIT(bp->addr),
+ MEM2BIT(bp->addr + bp->size) - 1);
+ bp->next = pool_mem;
+ pool_mem = bp;
+ }
+ pusht = 1;
+ break;
+ case KWD_CARD:
+ /* Card definition. */
+ parse_card(DT_VERS);
+ break;
+ case KWD_FUNCTION:
+ /* Function definition. */
+ parse_card(DT_FUNC);
+ break;
+ case KWD_DEBUGLEVEL:
+ i = debuglevel_tok(0);
+ if (i > 0)
+ debug_level = i;
+ break;
+ case KWD_INCLUDE:
+ incl = newstr(next_tok());
+ file_include(incl);
+ break;
+ default:
+ error("syntax error");
+ pusht = 0;
+ if (errors++ >= MAXERRORS) {
+ error("too many errors, giving up");
+ return;
+ }
+ break;
+ }
+}
+
+/*
+ * Parse a card definition.
+ */
+static void
+parse_card(int deftype)
+{
+ char *man, *vers, *tmp;
+ char *add_info;
+ unsigned char index_type;
+ struct card *cp;
+ int i, iosize;
+ struct card_config *confp, *lastp;
+ struct ether *ether;
+
+ confp = 0;
+ cp = xmalloc(sizeof(*cp));
+ cp->deftype = deftype;
+ switch (deftype) {
+ case DT_VERS:
+ man = newstr(next_tok());
+ vers = newstr(next_tok());
+ add_info = newstr(next_tok());
+ if (keyword(add_info)) {
+ pusht = 1;
+ free(add_info);
+ cp->add_info1 = NULL;
+ cp->add_info2 = NULL;
+ } else {
+ cp->add_info1 = add_info;
+ add_info = newstr(next_tok());
+ if (keyword(add_info)) {
+ pusht = 1;
+ free(add_info);
+ cp->add_info2 = NULL;
+ } else {
+ cp->add_info2 = add_info;
+ }
+ }
+ cp->manuf = man;
+ cp->version = vers;
+ cp->logstr = NULL;
+ asprintf(&cp->logstr, "%s (%s)", man, vers);
+ cp->func_id = 0;
+ break;
+ case DT_FUNC:
+ cp->manuf = NULL;
+ cp->version = NULL;
+ cp->logstr = NULL;
+ cp->func_id = (u_char) func_tok();
+ break;
+ default:
+ fprintf(stderr, "parse_card: unknown deftype %d\n", deftype);
+ exit(1);
+ }
+ cp->reset_time = 50;
+ cp->next = 0;
+ if (!last_card) {
+ cards = last_card = cp;
+ } else {
+ last_card->next = cp;
+ last_card = cp;
+ }
+ for (;;) {
+ switch (keyword(next_tok())) {
+ case KWD_CONFIG:
+ /* config */
+ i = config_tok(&index_type);
+ if (i == -1) {
+ error("illegal card config index");
+ break;
+ }
+ confp = xmalloc(sizeof(*confp));
+ man = next_tok();
+ confp->driver = new_driver(man);
+ confp->irq = irq_tok(1);
+ confp->flags = num_tok();
+ if (confp->flags == -1) {
+ pusht = 1;
+ confp->flags = 0;
+ }
+ if (confp->irq < 0 || confp->irq > 15) {
+ error("illegal card IRQ value");
+ break;
+ }
+ confp->index = i & 0x3F;
+ confp->index_type = index_type;
+
+ /*
+ * If no valid driver for this config, then do not save
+ * this configuration entry.
+ */
+ if (confp->driver) {
+ if (cp->config == 0)
+ cp->config = confp;
+ else {
+ for (lastp = cp->config; lastp->next;
+ lastp = lastp->next);
+ lastp->next = confp;
+ }
+ } else
+ free(confp);
+ break;
+ case KWD_RESET:
+ /* reset */
+ i = num_tok();
+ if (i == -1) {
+ error("illegal card reset time");
+ break;
+ }
+ cp->reset_time = i;
+ break;
+ case KWD_ETHER:
+ /* ether */
+ ether = xmalloc(sizeof(*ether));
+ ether->type = ETHTYPE_GENERIC;
+ tmp = next_tok();
+ if (strcmp("attr2", tmp) == 0)
+ ether->type = ETHTYPE_ATTR2;
+ else {
+ pusht = 1;
+ ether->value = num_tok();
+ if (ether->value == -1) {
+ error("illegal ether address offset");
+ free(ether);
+ break;
+ }
+ }
+ ether->next = cp->ether;
+ cp->ether = ether;
+ break;
+ case KWD_INSERT:
+ /* insert */
+ addcmd(&cp->insert);
+ break;
+ case KWD_REMOVE:
+ /* remove */
+ addcmd(&cp->remove);
+ break;
+ case KWD_IOSIZE:
+ /* iosize */
+ iosize = iosize_tok();
+ if (!iosize) {
+ error("Illegal cardio arguments");
+ break;
+ }
+ if (!confp) {
+ error("iosize should be placed after config");
+ break;
+ }
+ cp->iosize = iosize;
+ break;
+ case KWD_LOGSTR:
+ free(cp->logstr);
+ cp->logstr = newstr(next_tok());
+ break;
+ default:
+ pusht = 1;
+ return;
+ }
+ }
+}
+
+/*
+ * Generate a new driver structure. If one exists, use
+ * that one after confirming the correct class.
+ */
+static struct driver *
+new_driver(char *name)
+{
+ struct driver *drvp;
+ char *p;
+
+ for (drvp = drivers; drvp; drvp = drvp->next)
+ if (strcmp(drvp->name, name) == 0)
+ return (drvp);
+ drvp = xmalloc(sizeof(*drvp));
+ drvp->next = drivers;
+ drivers = drvp;
+ drvp->name = newstr(name);
+ drvp->kernel = newstr(name);
+ drvp->unit = -1;
+ p = drvp->kernel;
+ while (*p++)
+ if (*p >= '0' && *p <= '9') {
+ drvp->unit = atoi(p);
+ *p = 0;
+ break;
+ }
+#ifdef DEBUG
+ printf("Drv %s%d created\n", drvp->kernel, drvp->unit);
+#endif
+ return (drvp);
+}
+
+
+/*
+ * Parse one I/O block.
+ */
+static struct allocblk *
+ioblk_tok(int force)
+{
+ struct allocblk *io;
+ int i, j;
+
+ /* ignore the keyword to allow separete blocks in multiple lines */
+ if (keyword(next_tok()) != KWD_IO) {
+ pusht = 1;
+ }
+
+ if ((i = num_tok()) >= 0) {
+ if (strcmp("-", next_tok()) || (j = num_tok()) < 0 || j < i) {
+ error("I/O block format error");
+ return (0);
+ }
+ io = xmalloc(sizeof(*io));
+ io->addr = i;
+ io->size = j - i + 1;
+ if (j > IOPORTS) {
+ error("I/O port out of range");
+ if (force) {
+ free(io);
+ io = 0;
+ } else
+ io->addr = io->size = 0;
+ }
+ return (io);
+ }
+ if (force)
+ error("illegal or missing I/O block spec");
+ return (0);
+}
+
+/*
+ * Parse a memory block.
+ */
+static struct allocblk *
+memblk_tok(int force)
+{
+ struct allocblk *mem;
+ int i, j;
+
+ /* ignore the keyword to allow separete blocks in multiple lines */
+ if (keyword(next_tok()) != KWD_MEMORY) {
+ pusht = 1;
+ }
+
+ if ((i = num_tok()) >= 0) {
+ if ((j = num_tok()) < 0)
+ error("illegal memory block");
+ else {
+ mem = xmalloc(sizeof(*mem));
+ mem->addr = i & ~(MEMUNIT - 1);
+ mem->size = (j + MEMUNIT - 1) & ~(MEMUNIT - 1);
+ if (i < MEMSTART || (i + j) > MEMEND) {
+ error("memory address out of range");
+ if (force) {
+ free(mem);
+ mem = 0;
+ } else
+ mem->addr = mem->size = 0;
+ }
+ return (mem);
+ }
+ }
+ if (force)
+ error("illegal or missing memory block spec");
+ return (0);
+}
+
+/*
+ * IRQ token. Must be number > 0 && < 16.
+ * If force is set, IRQ must exist, and can also be '?' or 'any'.
+ */
+static int
+irq_tok(int force)
+{
+ int i;
+
+ /* ignore the keyword to allow separete blocks in multiple lines */
+ if (keyword(next_tok()) != KWD_IRQ) {
+ pusht = 1;
+ }
+
+ if (strcmp("?", next_tok()) == 0 && force)
+ return (0);
+ /* old PAO syntax -- people are still using it! */
+ if (strcmp("any", next_tok()) == 0 && force)
+ return (0);
+ pusht = 1;
+ i = num_tok();
+ if (i > 0 && i < 16)
+ return (i);
+ if (force)
+ error("illegal IRQ value");
+ return (-1);
+}
+
+/*
+ * Config index token
+ */
+static int
+config_tok(unsigned char *index_type)
+{
+ if (strcmp("default", next_tok()) == 0) {
+ *index_type = DEFAULT_INDEX;
+ return 0;
+ }
+ pusht = 1;
+ if (strcmp("auto", next_tok()) == 0) {
+ *index_type = AUTO_INDEX;
+ return 0;
+ }
+ pusht = 1;
+ *index_type = NORMAL_INDEX;
+ return num_tok();
+}
+/*
+ * Function ID token
+ */
+static int
+func_tok(void)
+{
+ if (strcmp("serial", next_tok()) == 0)
+ return 2;
+ pusht = 1;
+ if (strcmp("fixed_disk", next_tok()) == 0)
+ return 4;
+ pusht = 1;
+ return num_tok();
+}
+
+
+/*
+ * debuglevel token. Must be between 0 and 9.
+ */
+static int
+debuglevel_tok(int force)
+{
+ int i;
+
+ i = num_tok();
+ if (i >= 0 && i <= 9)
+ return (i);
+ return (-1);
+}
+
+/*
+ * iosize token
+ * iosize {<size>|auto}
+ */
+static int
+iosize_tok(void)
+{
+ int iosize = 0;
+ if (strcmp("auto", next_tok()) == 0)
+ iosize = -1; /* wildcard */
+ else {
+ pusht = 1;
+ iosize = num_tok();
+ if (iosize == -1)
+ return 0;
+ }
+#ifdef DEBUG
+ if (doverbose)
+ printf("iosize: size=%x\n", iosize);
+#endif
+ return iosize;
+}
+
+
+/*
+ * search the table for a match.
+ */
+static int
+keyword(char *str)
+{
+ char **s;
+ int i = 1;
+
+ for (s = keys; *s; s++, i++)
+ if (strcmp(*s, str) == 0)
+ return (i);
+
+ /* search keyword aliases too */
+ for (i = 0; key_aliases[i].key ; i++)
+ if (strcmp(key_aliases[i].alias, str) == 0)
+ return (key_aliases[i].key);
+
+ return (0);
+}
+
+/*
+ * addcmd - Append the command line to the list of
+ * commands.
+ */
+static void
+addcmd(struct cmd **cp)
+{
+ struct cmd *ncp;
+ char *s = getline();
+
+ if (*s) {
+ ncp = xmalloc(sizeof(*ncp));
+ ncp->line = s;
+ while (*cp)
+ cp = &(*cp)->next;
+ *cp = ncp;
+ }
+
+}
+
+static void
+error(char *msg)
+{
+ pusht = 1;
+ logmsg("%s: %s at line %d, near %s\n",
+ filename, msg, lineno, next_tok());
+ pusht = 1;
+}
+
+static int last_char;
+
+static int
+get(void)
+{
+ int c;
+
+ if (pushc)
+ c = pushc;
+ else
+ c = getc(in);
+ pushc = 0;
+ while (c == '\\') {
+ c = getc(in);
+ switch (c) {
+ case '#':
+ return (last_char = c);
+ case '\n':
+ lineno++;
+ c = getc(in);
+ continue;
+ }
+ pushc = c;
+ return ('\\');
+ }
+ if (c == '\n')
+ lineno++;
+ if (c == '#')
+ while (((c = get()) != '\n') && (c != EOF));
+ return (last_char = c);
+}
+
+/*
+ * num_tok - expecting a number token. If not a number,
+ * return -1.
+ * Handles octal (who uses octal anymore?)
+ * hex
+ * decimal
+ * Looks for a 'k' at the end of decimal numbers
+ * and multiplies by 1024.
+ */
+static int
+num_tok(void)
+{
+ char *s = next_tok(), c;
+ int val = 0, base;
+
+ base = 10;
+ c = *s++;
+ if (c == '0') {
+ base = 8;
+ c = *s++;
+ if (c == '\0') return 0;
+ else if (c == 'x' || c == 'X') {
+ c = *s++;
+ base = 16;
+ }
+ }
+ do {
+ switch (c) {
+ case 'k':
+ case 'K':
+ if (val && base == 10 && *s == 0)
+ return (val * 1024);
+ return (-1);
+ default:
+ return (-1);
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = val * base + c - '0';
+ break;
+
+ case '8':
+ case '9':
+ if (base == 8)
+ return (-1);
+ else
+ val = val * base + c - '0';
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ if (base == 16)
+ val = val * base + c - 'a' + 10;
+ else
+ return (-1);
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (base == 16)
+ val = val * base + c - 'A' + 10;
+ else
+ return (-1);
+ break;
+ }
+ } while ((c = *s++) != 0);
+ return (val);
+}
+
+static char *_next_tok(void);
+
+static char *
+next_tok(void)
+{
+ char *s = _next_tok();
+#if 0
+ printf("Tok = %s\n", s);
+#endif
+ return (s);
+}
+
+/*
+ * get one token. Handles string quoting etc.
+ */
+static char *
+_next_tok(void)
+{
+ static char buf[1024];
+ char *p = buf, instr = 0;
+ int c;
+
+ if (pusht) {
+ pusht = 0;
+ return (buf);
+ }
+ for (;;) {
+ c = get();
+ switch (c) {
+ default:
+ *p++ = c;
+ break;
+ case '"':
+ if (instr) {
+ *p++ = 0;
+ return (buf);
+ }
+ instr = 1;
+ break;
+ case '\n':
+ if (instr) {
+ error("unterminated string");
+ break;
+ }
+ case ' ':
+ case '\t':
+ /* Eat whitespace unless in a string. */
+ if (!instr) {
+ if (p != buf) {
+ *p++ = 0;
+ return (buf);
+ }
+ } else
+ *p++ = c;
+ break;
+ case '-':
+ case '?':
+ case '*':
+ /* Special characters that are tokens on their own. */
+ if (instr)
+ *p++ = c;
+ else {
+ if (p != buf)
+ pushc = c;
+ else
+ *p++ = c;
+ *p++ = 0;
+ return (buf);
+ }
+ break;
+ case EOF:
+ if (includes) {
+ fclose(in);
+ /* go back to previous config file */
+ includes--;
+ in = configfiles[includes].filep;
+ filename = configfiles[includes].filename;
+ lineno = configfiles[includes].lineno;
+ return _next_tok(); /* recursive */
+ }
+ if (p != buf) {
+ *p++ = 0;
+ return (buf);
+ }
+ strcpy(buf, "__EOF__");
+ return (buf);
+ }
+ }
+}
+
+/*
+ * get the rest of the line. If the
+ * last character scanned was a newline, then
+ * return an empty line. If this isn't checked, then
+ * a getline may incorrectly return the next line.
+ */
+static char *
+getline(void)
+{
+ char buf[1024], *p = buf;
+ int c, i = 0;
+
+ if (last_char == '\n')
+ return (newstr(""));
+ do {
+ c = get();
+ } while (c == ' ' || c == '\t');
+ for (; c != '\n' && c != EOF; c = get())
+ if (i++ < sizeof(buf) - 10)
+ *p++ = c;
+ *p = 0;
+ return (newstr(buf));
+}
+
+/*
+ * Include configuration file
+ */
+static void
+file_include(char *incl)
+{
+ int i, included;
+ FILE *fp;
+
+ /* check nesting overflow */
+ if (includes >= MAXINCLUDES) {
+ if (debug_level >= 1) {
+ logmsg("%s: include nesting overflow "
+ "at line %d, near %s\n", filename, lineno, incl);
+ }
+ free(incl);
+ goto out;
+ }
+
+ /* check recursive inclusion */
+ for (i = 0, included = 0; i <= includes; i++) {
+ if (strcmp(incl, configfiles[i].filename) == 0) {
+ included = 1;
+ break;
+ }
+ }
+ if (included == 1) {
+ if (debug_level >= 1) {
+ logmsg("%s: can't include the same file twice "
+ "at line %d, near %s\n", filename, lineno, incl);
+ }
+ free(incl);
+ goto out;
+ }
+
+ if (!(fp = fopen(incl, "r"))) {
+ if (debug_level >= 1) {
+ logmsg("%s: can't open include file "
+ "at line %d, near %s\n", filename, lineno, incl);
+ }
+ free(incl);
+ goto out;
+ }
+
+ /* save line number of the current config file */
+ configfiles[includes].lineno = lineno;
+ lineno = 1;
+
+ /* now we start parsing new config file */
+ includes++;
+ in = configfiles[includes].filep = fp;
+ filename = configfiles[includes].filename = incl;
+out:
+ return;
+}
diff --git a/usr.sbin/pccard/pccardd/pccard.conf.5 b/usr.sbin/pccard/pccardd/pccard.conf.5
new file mode 100644
index 0000000..6536de8
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccard.conf.5
@@ -0,0 +1,311 @@
+.\"
+.\" Copyright (c) 1994 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$
+.\"
+.Dd November 2, 1994
+.Dt PCCARD.CONF 5
+.Os
+.Sh NAME
+.Nm pccard.conf
+.Nd
+.Xr pccardd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr pccardd 8
+PC-CARD slot management daemon.
+It provides information to allow card
+identification, and the matching of drivers (along
+with driver resources) to the PC-CARD cards.
+.Pp
+There are four basic elements within the configuration file;
+An optional
+.Em "resource pool"
+preceding the other sections,
+and one or more
+.Em "card identifiers" ,
+and
+.Em "device instances" .
+The latter two may appear in any order, and may be
+interspersed as desired.
+.Pp
+The
+.Pa /etc/pccard.conf
+file is included from the file
+.Pa /etc/defaults/pccard.conf ,
+which contains the default resource pool settings and
+pccard identifiers database.
+The user specific configuration can be specified in
+.Pa /etc/pccard.conf
+when the user wishes to override these defaults and/or
+add additional entries.
+.Pp
+Each PC-CARD card contains configuration tuples that provide
+the manufacturer and card version; these are used
+to identify the card specification in the configuration
+file, and from this find a driver that can be used to
+interface to the particular card.
+There is a many-to-one mapping
+between cards to drivers i.e a single driver may interface to
+multiple types of cards.
+To aid this, card parameters may be
+specified separately from the driver to initialize the card or
+extract (in the case of a network card) an Ethernet address.
+.Pp
+Once a driver is allocated to a card, it stays
+allocated to that particular card.
+However, multiple instances of the same type of driver can be
+configured, so that if two cards are plugged in that map to a
+similar type of driver, other driver instances of the same name
+can be configured.
+.Pp
+The
+.Em insert
+and
+.Em remove
+commands allow a shell command line to be executed.
+The command to be executed is the rest of the line after
+the keyword.
+The line can be continued using a backslash.
+A simple
+macro substitution allows the current kernel device name
+.Em ( $device )
+and
+network card Ethernet address
+.Em ( $ether )
+to be inserted into the command line.
+.Xr pccardd 8
+uses the
+.Xr system 3
+subroutine to execute the command line.
+.Pp
+.Xr pccardd 8
+will use syslog to announce the insertion and removal of cards.
+It uses either the string set by the
+.Em logstr
+command, or the manufacturer and card version strings if none has
+been set.
+.Pp
+Numeric values may be expressed as octal, hex or decimal.
+If a decimal number has
+.Em k
+or
+.Em K
+appended to it, the value is multiplied by 1024. Names may be
+quoted using double quotes if spaces are required.
+A hash character comments out the rest of the line.
+.Ss "Resource pool"
+The (optional) section specifies a pool of system resources
+such as ISA bus memory address space, Input/Output ports and
+interrupt request numbers.
+This resource pool is used
+to allocate address space and interrupt numbers dynamically
+according to the requirements specified in each driver
+description.
+.Pp
+The syntax of the resources is as follows:
+.Pp
+.Dl io Ar start - end ...
+.Dl memory Ar address size ...
+.Dl irq Ar irq-number ...
+.Pp
+Each of the statements define I/O, memory or IRQ
+blocks that can be used to allocate to drivers when
+they are initialized.
+.Pp
+The syntax of the debuglevel parameter:
+.Pp
+.Dl debuglevel Ar level
+.Pp
+Multiple lines of any of the above statements may be
+present to allow separate blocks of each resource to be
+defined.
+.Ss "Card Identifiers"
+The syntax for card identifiers is:
+.Pp
+.Dl card Ar manufacturer version [ add_info1 [ add_info2 ]]
+.Dl config Ar index driver interrupt [ flags ]
+.Dl ether Ar offset
+.Dl reset Ar time
+.Dl iosize Ar size
+.Dl memsize Ar size
+.Dl insert Ar command
+.Dl remove Ar command
+.Dl logstr Ar string
+.Pp
+The first line is mandatory;
+the latter statements are optional and can appear in
+any order.
+There may be multiple
+.Em config
+lines.
+The
+.Em card
+parameters are the Manufacturer name, card version and
+additional information add_info1, add_info2 that
+is used to match the values from the card's CIS memory.
+These parameters can be described in extended regular expression
+.Xr regex 3
+if the string is enclosed by '/' like "/.*/".
+Each of the expressions is evaluated with a character '^' at top.
+.Pp
+The
+.Em config
+parameters select the particular card's configuration index
+from the range available in the card's CIS, the driver that
+is to be associated with this configuration, and the interrupt
+level (if any) to be assigned.
+An optional set of flags may
+be assigned.
+In
+.Ar index ,
+specify either
+.Dq auto
+or
+.Dq default
+or the range available in the card's CIS.
+.Dq auto
+allows to allocate resources automatically with information
+from the CIS and status of using I/O resources.
+.Pp
+The optional
+.Em ether
+keyword is used when network cards have their physical Ethernet address
+located within the attribute memory of the card.
+The parameter of this
+statement indicates the offset within the attribute memory of the
+Ethernet address.
+This value can be used within insert/remove
+commands using the
+.Em $ether
+macro.
+.Pp
+The optional
+.Em reset
+keyword specifies reset duration at a card insertion in
+.Ar time
+milliseconds.
+Default is 100msec.
+.Pp
+.Em iosize
+and
+.Em memsize
+keywords are used with cards whose resources such as I/O ports and
+shared memory block are not specified in the CIS tuple.
+.Pp
+The
+.Em insert
+and
+.Em remove
+sections allow shell commands to be specified that are executed
+when the card is inserted or removed.
+Multiple
+.Em insert
+and
+.Em remove
+commands are allowed, and they are executed in the order they
+are listed.
+.Pp
+The
+.Em logstr
+command allows the user to set the string to be logged when this card is
+inserted or removed.
+If
+.Em logstr
+isn't specified, then the manufacturer and
+card version strings from the CIS are used to synthesize the string issued.
+.Ss "Wildcard entries"
+Following two wildcard entries of card identifiers are available
+for generic type of the cards:
+.Pp
+.Dl generic serial
+.Dl generic fixed_disk
+.Pp
+The keyword
+.Em serial
+matches
+.Dq Functional ID: Serial port/modem
+and
+.Em fixed_disk
+matches
+.Dq Fixed disk card .
+The syntax is the same as for
+.Em "card identifiers"
+but uses
+.Dq generic
+instead of
+.Dq card
+in the first line.
+These are defined at the bottom of
+.Nm
+so unmatched cards use the generic entries.
+The alias
+.Dq function
+can be used instead of
+.Dq generic ,
+this is supported due to historical reasons.
+.Sh EXAMPLES
+A typical configuration file may look like this:
+.Bd -literal
+#
+# Sample configuration file.
+#
+# Pool parameters.
+#
+io 0x280 - 0x2F0 0x300 - 0x360
+irq 5 6 8 9 10 15
+memory 0xd4000 96k
+memory 0xc4000 32k
+#
+# Card database.
+#
+card "RPTI LTD." "EP400" # NE2000 clone
+ ether 0x110
+ config 0x21 "ed0" 5
+ insert ifconfig $device physical $ether
+ insert ifconfig $device bean
+ remove ifconfig $device down
+
+card "XYZZY" "FAX/1.0"
+ config 0x30 "sio1" 11
+ insert echo start getty
+ remove echo stop getty
+
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/defaults/pccard.conf -compact
+.It Pa /etc/defaults/pccard.conf
+The
+.Xr pccardd 8
+default configuration file.
+.It Pa /etc/pccard.conf
+The
+user configuration file.
+.El
+.Sh SEE ALSO
+.Xr pccardd 8
diff --git a/usr.sbin/pccard/pccardd/pccardd.8 b/usr.sbin/pccard/pccardd/pccardd.8
new file mode 100644
index 0000000..649619c
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.8
@@ -0,0 +1,189 @@
+.\"
+.\" Copyright (c) 1994 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$
+.\"
+.Dd November 1, 1994
+.Dt PCCARDD 8
+.Os
+.Sh NAME
+.Nm pccardd
+.Nd PC-CARD (PCMCIA) management daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl v
+.Op Fl x
+.Op Fl z
+.Op Fl i Ar IRQ
+.Op Fl I
+.Op Fl f Ar configfile
+.Op Fl s Ar socket
+.Sh DESCRIPTION
+The
+.Nm
+utility is normally started at boot time, and manages the insertion
+and removal of PC-CARD cards.
+.Pp
+When started,
+.Nm
+will read the configuration file (default name
+.Pa /etc/defaults/pccard.conf
+which includes
+.Pa /etc/pccard.conf
+as the user configuration file)
+and scans the available PC-CARD slots for cards.
+The
+.Nm
+utility then waits for
+.Em "card events" ,
+such as the insertion of a new card or the removal
+of a card.
+.Pp
+When a card is inserted, the following
+actions are taken:
+.Bl -enum
+.It
+The kernel driver detects the card insertion and applies
+power to the card.
+.It
+The
+.Nm
+utility reads the
+.Em CIS
+data from the attribute memory of the card, and uses
+the manufacturer name and card version to match
+the card description in the configuration file.
+.It
+Once matched, a driver is allocated.
+.It
+Once a free driver and device instance is located,
+.Nm
+will (if required) allocate resources such as an ISA memory
+block and Input/Output ports from a common pool.
+.It
+The PC-CARD slot is configured with the I/O and memory
+contexts allocated, and the kernel driver is attached to
+this card.
+.It
+If the attach succeeds, then specific shell commands
+may be executed to configure the device, such as
+.Xr ifconfig 8
+to set up a network interface.
+Separate commands may be specified
+for each card, driver or device, and are executed in that order.
+.El
+.Pp
+When
+.Nm
+detects that a card has been removed, the following sequence occurs:
+.Bl -enum
+.It
+The shell commands associated with card removal are executed.
+These
+are intended to reset any device associated with the removed card.
+Separate commands may exist for card, driver and device instances.
+.It
+The PC-CARD slot resources are freed.
+.El
+.Pp
+Once a card/driver instance is configured, the resources
+bound to that instance are remembered, and if the card is removed
+and reinserted, the same driver is allocated.
+The reasons are mostly historical.
+.Pp
+SIGHUP causes
+.Nm
+to reload the configuration files.
+.Pp
+The start options understood by
+.Nm
+are:
+.Bl -tag -width Ds
+.It Fl d
+Do not run as a daemon, but run in the foreground and
+display error messages.
+.It Fl v
+After reading the configuration file, print out a summary
+of it.
+.It Fl x
+Exits immediately after the cards have been probed and attached.
+This is primarily useful in embedded applications where it is
+desirable to use
+.Nm
+to start PC-CARD devices but prohibitive memory-wise to leave the
+.Nm
+process running.
+.It Fl z
+Delays running as a daemon until after the cards have been probed and attached.
+.It Fl I
+Don't get a list of free IRQs from kernel.
+.It Fl i Ar IRQ
+Configures an available IRQ. It overrides the "irq" line in
+.Pa /etc/defaults/pccard.conf
+and
+.Pa /etc/pccard.conf .
+.It Fl f Ar configfile
+Specifies a different configuration file to be used
+in placed of the default file
+.Pa /etc/defaults/pccard.conf .
+The file format is detailed in
+.Xr pccard.conf 5 ,
+and lists the PC-CARD cards recognized by
+.Nm ,
+and the kernel drivers and devices that are used to
+interface to the card.
+.It Fl s Ar socket
+Specifies a path to a control socket.
+The default is
+.Pa /var/tmp/.pccardd .
+.El
+.Sh FILES
+.Bl -tag -width /etc/defaults/pccard.conf -compact
+.It Pa /etc/defaults/pccard.conf
+default configuration file
+.It Pa /etc/pccard.conf
+user configuration file
+.It Pa /var/tmp/.pccardd
+default control socket
+.It Pa /var/run/pccardd.pid
+process id of the currently running
+.Nm
+.El
+.Sh SEE ALSO
+.Xr pccard.conf 5 ,
+.Xr ifconfig 8
+.Sh AUTHORS
+Developed by
+.An Andrew McRae Aq andrew@mega.com.au .
+.Sh BUGS
+The
+.Nm
+utility can set up card parameters, but cannot guarantee that
+particular drivers can work with the card.
+.Pp
+Removing cards may cause problems if system resources
+have been associated with the card, such as network
+mounted file systems.
diff --git a/usr.sbin/pccard/pccardd/pccardd.c b/usr.sbin/pccard/pccardd/pccardd.c
new file mode 100644
index 0000000..11d0716
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.c
@@ -0,0 +1,285 @@
+/*
+ * 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 */
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#define EXTERN
+#include "cardd.h"
+
+char *config_file = "/etc/defaults/pccard.conf";
+static char *pid_file = "/var/run/pccardd.pid";
+
+/*
+ * pathname of UNIX-domain socket
+ */
+static char *socket_name = "/var/tmp/.pccardd";
+static char *sock = 0;
+static int server_sock;
+
+/* SIGHUP signal handler */
+static void
+restart(void)
+{
+ bitstr_t bit_decl(io_inuse, IOPORTS);
+ bitstr_t bit_decl(mem_inuse, MEMBLKS);
+ int irq_inuse[16];
+ int i;
+ struct sockaddr_un sun;
+
+ bit_nclear(io_inuse, 0, IOPORTS-1);
+ bit_nclear(mem_inuse, 0, MEMBLKS-1);
+ bzero(irq_inuse, sizeof(irq_inuse));
+
+ /* compare the initial and current state of resource pool */
+ for (i = 0; i < IOPORTS; i++) {
+ if (bit_test(io_init, i) == 1 && bit_test(io_avail, i) == 0) {
+ if (debug_level >= 1) {
+ logmsg("io 0x%x seems to be in use\n", i);
+ }
+ bit_set(io_inuse, i);
+ }
+ }
+ for (i = 0; i < MEMBLKS; i++) {
+ if (bit_test(mem_init, i) == 1 && bit_test(mem_avail, i) == 0) {
+ if (debug_level >= 1) {
+ logmsg("mem 0x%x seems to be in use\n", i);
+ }
+ bit_set(mem_inuse, i);
+ }
+ }
+ for (i = 0; i < 16; i++) {
+ if (irq_init[i] == 1 && pool_irq[i] == 0) {
+ if (debug_level >= 1) {
+ logmsg("irq %d seems to be in use\n", i);
+ }
+ irq_inuse[i] = 1;
+ }
+ }
+
+ readfile(config_file);
+
+ /* reflect used resources to managed resource pool */
+ for (i = 0; i < IOPORTS; i++) {
+ if (bit_test(io_inuse, i) == 1) {
+ bit_clear(io_avail, i);
+ }
+ }
+ for (i = 0; i < MEMBLKS; i++) {
+ if (bit_test(mem_inuse, i) == 1) {
+ bit_clear(mem_avail, i);
+ }
+ }
+ for (i = 0; i < 16; i++) {
+ if (irq_inuse[i] == 1) {
+ pool_irq[i] = 0;
+ }
+ }
+ close(server_sock);
+ if ((server_sock = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0)
+ die("socket failed");
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ if (sock) {
+ socket_name = sock;
+ }
+ strcpy(sun.sun_path, socket_name);
+ slen = SUN_LEN(&sun);
+ (void)unlink(socket_name);
+ if (bind(server_sock, (struct sockaddr *) & sun, slen) < 0)
+ die("bind failed");
+ chown(socket_name, 0, 5); /* XXX - root.operator */
+ chmod(socket_name, 0660);
+ set_socket(server_sock);
+}
+
+/* SIGTERM/SIGINT signal handler */
+static void
+term(int sig)
+{
+ logmsg("pccardd terminated: signal %d received", sig);
+ (void)unlink(pid_file);
+ exit(0);
+}
+
+static void
+write_pid()
+{
+ FILE *fp = fopen(pid_file, "w");
+
+ if (fp) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+int doverbose = 0;
+/*
+ * mainline code for cardd
+ */
+int
+main(int argc, char *argv[])
+{
+ struct slot *sp;
+ int count, dodebug = 0;
+ int probeonly = 0;
+ int delay = 0;
+ int irq_arg[16];
+ int irq_specified = 0;
+ int i;
+ struct sockaddr_un sun;
+#define COM_OPTS ":Idf:i:s:vxz"
+
+ bzero(irq_arg, sizeof(irq_arg));
+ use_kern_irq = 1;
+ debug_level = 0;
+ pccard_init_sleep = 5000000;
+ cards = last_card = 0;
+ while ((count = getopt(argc, argv, COM_OPTS)) != -1) {
+ switch (count) {
+ case 'I':
+ use_kern_irq = 0;
+ break;
+ case 'd':
+ setbuf(stdout, 0);
+ setbuf(stderr, 0);
+ dodebug = 1;
+ break;
+ case 'v':
+ doverbose = 1;
+ break;
+ case 'f':
+ config_file = optarg;
+ break;
+ case 'i':
+ /* configure available irq */
+ if (sscanf(optarg, "%d", &i) != 1) {
+ fprintf(stderr, "%s: -i number\n", argv[0]);
+ exit(1);
+ }
+ irq_arg[i] = 1;
+ irq_specified = 1;
+ break;
+ case 's':
+ sock = optarg;
+ break;
+ case 'x':
+ probeonly = 1;
+ break;
+ case 'z':
+ delay = 1;
+ break;
+ case ':':
+ die("no config file argument");
+ break;
+ case '?':
+ die("illegal option");
+ break;
+ }
+ }
+#ifdef DEBUG
+ dodebug = 1;
+#endif
+ io_avail = bit_alloc(IOPORTS); /* Only supports ISA ports */
+ io_init = bit_alloc(IOPORTS);
+
+ /* Mem allocation done in MEMUNIT units. */
+ mem_avail = bit_alloc(MEMBLKS);
+ mem_init = bit_alloc(MEMBLKS);
+ readfile(config_file);
+ if (irq_specified) {
+ bcopy(irq_arg, pool_irq, sizeof(irq_arg));
+ bcopy(irq_arg, irq_init, sizeof(irq_arg));
+ }
+ if (doverbose)
+ dump_config_file();
+ log_setup();
+ if (!dodebug && !delay && !probeonly)
+ if (daemon(0, 0))
+ die("fork failed");
+ slots = readslots();
+ if (slots == 0)
+ die("no PC-CARD slots");
+ if (probeonly)
+ exit(0);
+ if (delay)
+ if (daemon(0, 0))
+ die("fork failed");
+ logmsg("pccardd started", NULL);
+ write_pid();
+
+ if ((server_sock = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0)
+ die("socket failed");
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ if (sock) {
+ socket_name = sock;
+ }
+ strcpy(sun.sun_path, socket_name);
+ slen = SUN_LEN(&sun);
+ (void)unlink(socket_name);
+ if (bind(server_sock, (struct sockaddr *) & sun, slen) < 0)
+ die("bind failed");
+ chown(socket_name, 0, 5); /* XXX - root.operator */
+ chmod(socket_name, 0660);
+ set_socket(server_sock);
+
+ (void)signal(SIGINT, dodebug ? term : SIG_IGN);
+ (void)signal(SIGTERM, term);
+ (void)signal(SIGHUP, (void (*)(int))restart);
+
+ for (;;) {
+ fd_set rmask, emask;
+ FD_ZERO(&emask);
+ FD_ZERO(&rmask);
+ for (sp = slots; sp; sp = sp->next)
+ FD_SET(sp->fd, &emask);
+ FD_SET(server_sock, &rmask);
+ count = select(32, &rmask, 0, &emask, 0);
+ if (count == -1) {
+ logerr("select");
+ continue;
+ }
+ if (count) {
+ for (sp = slots; sp; sp = sp->next)
+ if (FD_ISSET(sp->fd, &emask))
+ slot_change(sp);
+ if (FD_ISSET(server_sock, &rmask))
+ process_client();
+ }
+ }
+}
diff --git a/usr.sbin/pccard/pccardd/readcis.c b/usr.sbin/pccard/pccardd/readcis.c
new file mode 100644
index 0000000..73b72ef
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.c
@@ -0,0 +1,790 @@
+/*
+ * 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 <pccard/cardinfo.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+#ifdef RATOCLAN
+static int rex5588 = 0;
+#endif
+int isdumpcisfile = 0;
+
+static int read_attr(int, char *, int);
+static int ck_linktarget(int, off_t, int);
+static void cis_info(struct cis *, unsigned char *, int);
+static void device_desc(unsigned char *, int, struct dev_mem *);
+static void config_map(struct cis *, unsigned char *, int);
+static void cis_config(struct cis *, unsigned char *, int);
+static void cis_manuf_id(struct cis *, unsigned char *, int);
+static void cis_func_id(struct cis *, unsigned char *, int);
+static void cis_network_ext(struct cis *, unsigned char *, 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", 0x00, 0},
+ {"Common memory descriptor", 0x01, 255},
+ {"Long link to next chain for CardBus", 0x02, 255},
+ {"Indirect access", 0x03, 255},
+ {"Configuration map for CardBus", 0x04, 255},
+ {"Configuration entry for CardBus", 0x05, 255},
+ {"Long link to next chain for MFC", 0x06, 255},
+ {"Base address register for CardBus", 0x07, 6},
+ {"Checksum", 0x10, 5},
+ {"Long link to attribute memory", 0x11, 4},
+ {"Long link to common memory", 0x12, 4},
+ {"Link target", 0x13, 3},
+ {"No link", 0x14, 0},
+ {"Version 1 info", 0x15, 255},
+ {"Alternate language string", 0x16, 255},
+ {"Attribute memory descriptor", 0x17, 255},
+ {"JEDEC descr for common memory", 0x18, 255},
+ {"JEDEC descr for attribute memory", 0x19, 255},
+ {"Configuration map", 0x1A, 255},
+ {"Configuration entry", 0x1B, 255},
+ {"Other conditions for common memory", 0x1C, 255},
+ {"Other conditions for attribute memory", 0x1D, 255},
+ {"Geometry info for common memory", 0x1E, 255},
+ {"Geometry info for attribute memory", 0x1F, 255},
+ {"Manufacturer ID", 0x20, 4},
+ {"Functional ID", 0x21, 2},
+ {"Functional EXT", 0x22, 255},
+ {"Software interleave", 0x23, 2},
+ {"Version 2 Info", 0x40, 255},
+ {"Data format", 0x41, 255},
+ {"Geometry", 0x42, 4},
+ {"Byte order", 0x43, 2},
+ {"Card init date", 0x44, 4},
+ {"Battery replacement", 0x45, 4},
+ {"Organization", 0x46, 255},
+ {"Terminator", 0xFF, 0},
+ {0, 0, 0}
+};
+
+/*
+ * After reading the tuples, decode the relevant ones.
+ */
+struct cis *
+readcis(int fd)
+{
+ struct tuple_list *tl;
+ struct tuple *tp;
+ struct cis *cp;
+
+ cp = xmalloc(sizeof(*cp));
+ cp->tlist = read_tuples(fd);
+ if (cp->tlist == 0)
+ return (NULL);
+
+ for (tl = cp->tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next) {
+#if 0
+ printf("tuple code = 0x%02x, data is\n", tp->code);
+ dump(tp->data, tp->length);
+#endif
+ switch (tp->code) {
+ case CIS_MEM_COMMON: /* 0x01 */
+ device_desc(tp->data, tp->length, &cp->common_mem);
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ cis_info(cp, tp->data, tp->length);
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ device_desc(tp->data, tp->length, &cp->attr_mem);
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ config_map(cp, tp->data, tp->length);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ cis_config(cp, tp->data, tp->length);
+ break;
+ case CIS_MANUF_ID: /* 0x20 */
+ cis_manuf_id(cp, tp->data, tp->length);
+ break;
+ case CIS_FUNC_ID: /* 0x21 */
+ cis_func_id(cp, tp->data, tp->length);
+ break;
+ case CIS_FUNC_EXT: /* 0x22 */
+ if (cp->func_id1 == 6) /* LAN adaptor */
+ cis_network_ext(cp, tp->data, tp->length);
+ break;
+ }
+ }
+ return (cp);
+}
+
+/*
+ * free_cis - delete cis entry.
+ */
+void
+freecis(struct cis *cp)
+{
+ struct cis_ioblk *io;
+ struct cis_memblk *mem;
+ struct cis_config *conf;
+ struct tuple *tp;
+ struct tuple_list *tl;
+
+ while ((tl = cp->tlist) != 0) {
+ cp->tlist = tl->next;
+ while ((tp = tl->tuples) != 0) {
+ tl->tuples = tp->next;
+ if (tp->data)
+ free(tp->data);
+ }
+ }
+
+ while ((conf = cp->conf) != 0) {
+ cp->conf = conf->next;
+ while ((io = conf->io) != 0) {
+ conf->io = io->next;
+ free(io);
+ }
+ while ((mem = conf->mem) != 0) {
+ conf->mem = mem->next;
+ free(mem);
+ }
+ free(conf);
+ }
+ free(cp);
+}
+
+/*
+ * Fills in CIS version data.
+ */
+static void
+cis_info(struct cis *cp, unsigned char *p, int len)
+{
+ cp->maj_v = *p++;
+ cp->min_v = *p++;
+ len -= 2;
+ if (cp->manuf) {
+ free(cp->manuf);
+ cp->manuf = NULL;
+ }
+ if (len > 1 && *p != 0xff) {
+ cp->manuf = strdup(p);
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ }
+ if (cp->vers) {
+ free(cp->vers);
+ cp->vers = NULL;
+ }
+ if (len > 1 && *p != 0xff) {
+ cp->vers = strdup(p);
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ } else {
+ cp->vers = strdup("[none]");
+ }
+ if (cp->add_info1) {
+ free(cp->add_info1);
+ cp->add_info1 = NULL;
+ }
+ if (len > 1 && *p != 0xff) {
+ cp->add_info1 = strdup(p);
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ } else {
+ cp->add_info1 = strdup("[none]");
+ }
+ if (cp->add_info2) {
+ free(cp->add_info2);
+ cp->add_info2 = NULL;
+ }
+ if (len > 1 && *p != 0xff)
+ cp->add_info2 = strdup(p);
+ else
+ cp->add_info2 = strdup("[none]");
+}
+
+static void
+cis_manuf_id(struct cis *cp, unsigned char *p, int len)
+{
+ if (len >= 4) {
+ cp->manufacturer = tpl16(p);
+ cp->product = tpl16(p+2);
+ if (len == 5)
+ cp->prodext = *(p+4); /* For xe driver */
+ } else {
+ cp->manufacturer=0;
+ cp->product=0;
+ cp->prodext=0;
+ }
+}
+/*
+ * Fills in CIS function ID.
+ */
+static void
+cis_func_id(struct cis *cp, unsigned char *p, int len)
+{
+ cp->func_id1 = *p++;
+ cp->func_id2 = *p++;
+}
+
+static void
+cis_network_ext(struct cis *cp, unsigned char *p, int len)
+{
+ int i;
+
+ switch (p[0]) {
+ case 4: /* Node ID */
+ if (len <= 2 || len < p[1] + 2)
+ return;
+
+ if (cp->lan_nid)
+ free(cp->lan_nid);
+ cp->lan_nid = xmalloc(p[1]);
+
+ for (i = 0; i <= p[1]; i++)
+ cp->lan_nid[i] = p[i + 1];
+ break;
+ }
+}
+
+/*
+ * "FUJITSU LAN Card (FMV-J182)" has broken CIS
+ */
+static int
+fmvj182_check(unsigned char *p)
+{
+ char manuf[BUFSIZ], vers[BUFSIZ];
+
+ p++; /* major version */
+ p++; /* minor version */
+ strncpy(manuf, p, sizeof(manuf) - 1);
+ while (*p++);
+ strncpy(vers, p, sizeof(vers) - 1);
+ if (!strcmp(manuf, "FUJITSU") && !strcmp(vers, "LAN Card(FMV-J182)"))
+ return 1;
+ else
+ return 0;
+}
+
+#ifdef RATOCLAN
+/*
+ * "RATOC LAN Card (REX-5588)" has broken CIS
+ */
+static int
+rex5588_check(unsigned char *p)
+{
+ char manuf[BUFSIZ], vers[BUFSIZ];
+
+ p++; /* major version */
+ p++; /* minor version */
+ strncpy(manuf, p, sizeof(manuf) - 1);
+ while (*p++);
+ strncpy(vers, p, sizeof(manuf) - 1);
+ if (!strcmp(manuf, "PCMCIA LAN MBH10304 ES"))
+ return 1;
+ else
+ return 0;
+}
+#endif
+
+#ifdef HSSYNTH
+/*
+ * Broken CIS for "HITACHI MICROCOMPUTER SYSTEM LTD." "MSSHVPC02"
+ */
+static int
+hss_check(unsigned char *p)
+{
+ char manuf[BUFSIZ], vers[BUFSIZ];
+
+ p++; /* major version */
+ p++; /* minor version */
+ strncpy(manuf, p, sizeof(manuf) - 1);
+ while (*p++);
+ strncpy(vers, p, sizeof(vers) - 1);
+ if (!strcmp(manuf, "HITACHI MICROCOMPUTER SYSTEMS LTD.")
+ && !strcmp(vers, "MSSHVPC02"))
+ return 1;
+ else
+ return 0;
+}
+#endif /* HSSYNTH */
+
+/*
+ * device_desc - decode device descriptor.
+ */
+static void
+device_desc(unsigned char *p, int len, struct dev_mem *dp)
+{
+ while (len > 0 && *p != 0xFF) {
+ dp->valid = 1;
+ dp->type = (*p & 0xF0) >> 4;
+ dp->wps = !!(*p & 0x8);
+ dp->speed = *p & 7;
+ p++;
+ if (*p != 0xFF) {
+ dp->addr = (*p >> 3) & 0xF;
+ dp->units = *p & 7;
+ }
+ p++;
+ len -= 2;
+ }
+}
+
+/*
+ * configuration map of card control register.
+ */
+static void
+config_map(struct cis *cp, unsigned char *p, int len)
+{
+ unsigned char *p1;
+ int rlen = (*p & 3) + 1;
+
+ p1 = p + 1;
+ cp->last_config = *p1++ & 0x3F;
+ cp->reg_addr = parse_num(rlen | 0x10, p1, &p1, 0);
+ cp->ccrs = *p1;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * CIS config entry - Decode and build configuration entry.
+ */
+static void
+cis_config(struct cis *cp, unsigned char *p, int len)
+{
+ int x;
+ int i, j;
+ struct cis_config *conf, *last;
+ unsigned char feat;
+
+ conf = xmalloc(sizeof(*conf));
+ if ((last = cp->conf) != 0) {
+ while (last->next)
+ last = last->next;
+ last->next = conf;
+ } else
+ cp->conf = conf;
+ conf->id = *p & 0x3F; /* Config index */
+#ifdef RATOCLAN
+ if (rex5588 && conf->id >= 0x08 && conf->id <= 0x1d)
+ conf->id |= 0x20;
+#endif
+ if (*p & 0x40) /* Default flag */
+ cp->def_config = conf;
+ if (*p++ & 0x80)
+ p++; /* Interface byte skip */
+ feat = *p++; /* Features byte */
+ for (i = 0; i < CIS_FEAT_POWER(feat); i++) {
+ unsigned char parms = *p++;
+
+ conf->pwr = 1;
+ for (j = 0; j < 8; j++)
+ if (parms & (1 << j))
+ while (*p++ & 0x80);
+ }
+ if (feat & CIS_FEAT_TIMING) {
+ conf->timing = 1;
+ i = *p++;
+ if (CIS_WAIT_SCALE(i) != 3)
+ p++;
+ if (CIS_READY_SCALE(i) != 7)
+ p++;
+ if (CIS_RESERVED_SCALE(i) != 7)
+ p++;
+ }
+ if (feat & CIS_FEAT_I_O) {
+ conf->iospace = 1;
+ if (CIS_IO_RANGE & *p)
+ conf->io_blks = CIS_IO_BLKS(p[1]) + 1;
+ conf->io_addr = CIS_IO_ADDR(*p);
+ conf->io_bus = (*p >> 5) & 3; /* CIS_IO_8BIT | CIS_IO_16BIT */
+ if (*p++ & CIS_IO_RANGE) {
+ struct cis_ioblk *io;
+ struct cis_ioblk *last_io = NULL;
+
+ i = CIS_IO_ADSZ(*p);
+ j = CIS_IO_BLKSZ(*p++);
+ for (x = 0; x < conf->io_blks; x++) {
+ io = xmalloc(sizeof(*io));
+ if (last_io)
+ last_io->next = io;
+ else
+ conf->io = io;
+ last_io = io;
+ io->addr = parse_num(i, p, &p, 0);
+ io->size = parse_num(j, p, &p, 1);
+ }
+ }
+ }
+ if (feat & CIS_FEAT_IRQ) {
+ conf->irq = 1;
+ conf->irqlevel = *p & 0xF;
+ conf->irq_flags = *p & 0xF0;
+ if (*p++ & CIS_IRQ_MASK) {
+ conf->irq_mask = tpl16(p);
+ p += 2;
+ }
+ }
+ switch (CIS_FEAT_MEMORY(feat)) {
+ case CIS_FEAT_MEM_NONE:
+ break;
+ case CIS_FEAT_MEM_LEN:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = tpl16(p) << 8;
+ break;
+ case CIS_FEAT_MEM_ADDR:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = tpl16(p) << 8;
+ conf->mem->address = tpl16(p + 2) << 8;
+ break;
+ case CIS_FEAT_MEM_WIN: {
+ struct cis_memblk *mem;
+ struct cis_memblk *last_mem = NULL;
+
+ conf->memspace = 1;
+ x = *p++;
+ conf->memwins = CIS_MEM_WINS(x);
+ for (i = 0; i < conf->memwins; i++) {
+ mem = xmalloc(sizeof(*mem));
+ if (last_mem)
+ last_mem->next = mem;
+ else
+ conf->mem = mem;
+ last_mem = mem;
+ mem->length = parse_num(CIS_MEM_LENSZ(x) | 0x10, p, &p, 0) << 8;
+ mem->address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10, p, &p, 0) << 8;
+ if (x & CIS_MEM_HOST) {
+ mem->host_address = parse_num(CIS_MEM_ADDRSZ(x) | 0x10,
+ p, &p, 0) << 8;
+ }
+ }
+ break;
+ }
+ }
+ if (feat & CIS_FEAT_MISC) {
+ conf->misc_valid = 1;
+ conf->misc = *p++;
+ }
+}
+
+/*
+ * 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 %qd (%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 && tlist->next == 0 &&
+ ck_linktarget(fd, (off_t) 0, 0)) {
+#ifdef DEBUG
+ printf("Reading long link at %qd (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ tlist->next = read_one_tuplelist(fd, 0, (off_t) 0);
+ }
+ 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;
+ int fmvj182 = 0;
+#ifdef HSSYNTH
+ int hss = 0;
+#endif /* HSSYNTH */
+
+ /* 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_attr(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_attr(fd, &length, 1) != 1) {
+ warn("CIS len read");
+ break;
+ }
+ total++;
+ if (fmvj182 && (code == 0x1b) && (length == 25))
+ length = 31;
+ }
+ 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_attr(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 (code == CIS_INFO_V1) {
+ /* Hack for broken CIS of FMV-J182 Ethernet card */
+ fmvj182 = fmvj182_check(tp->data);
+#ifdef RATOCLAN
+ /* Hack for RATOC LAN card */
+ rex5588 = rex5588_check(tp->data);
+#endif /* RATOCLAN */
+#ifdef HSSYNTH
+ /* Hack for Hitachi Speech Synthesis card */
+ hss = hss_check(tp->data);
+#endif /* HSSYNTH */
+ }
+ if (tinfo != NULL && (tinfo->length != 255 && tinfo->length > length)) {
+ printf("code %s ignored\n", tuple_name(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_attr(fd, blk, 5) != 5)
+ return (0);
+ if (blk[0] == 0x13 &&
+ 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);
+}
+
+static int
+read_attr(int fd, char *bp, int len)
+{
+ char blk[1024], *p = blk;
+ int i, l;
+
+ if (isdumpcisfile)
+ return (read(fd, bp, len));
+ if (len > sizeof(blk) / 2)
+ len = sizeof(blk) / 2;
+ l = i = read(fd, blk, len * 2);
+ if (i <= 0) {
+ printf("Read return %d bytes (expected %d)\n", i, len * 2);
+ return (i);
+ }
+ while (i > 0) {
+ *bp++ = *p++;
+ p++;
+ i -= 2;
+ }
+ return (l / 2);
+}
+
+/*
+ * 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);
+}
+
+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/pccard/pccardd/readcis.h b/usr.sbin/pccard/pccardd/readcis.h
new file mode 100644
index 0000000..8ebd745
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.h
@@ -0,0 +1,148 @@
+/*
+ * 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 {
+ char *name;
+ unsigned char code;
+ unsigned char length; /* 255 means variable length */
+};
+
+/*
+ * Memory device descriptor.
+ */
+struct dev_mem {
+ unsigned char valid;
+ unsigned char type;
+ unsigned char speed;
+ unsigned char wps;
+ unsigned char addr;
+ unsigned char units;
+};
+
+/*
+ * One I/O structure describing a possible I/O map
+ * of the card.
+ */
+struct cis_ioblk {
+ struct cis_ioblk *next;
+ unsigned int addr;
+ unsigned int size;
+};
+
+/*
+ * A structure storing a memory map for the card.
+ */
+struct cis_memblk {
+ struct cis_memblk *next;
+ unsigned int address;
+ unsigned int length;
+ unsigned int host_address;
+};
+
+/*
+ * One configuration entry for the card.
+ */
+struct cis_config {
+ struct cis_config *next;
+ unsigned int pwr:1; /* Which values are defined. */
+ unsigned int timing:1;
+ unsigned int iospace:1;
+ unsigned int irq:1;
+ unsigned int memspace:1;
+ unsigned int misc_valid:1;
+ unsigned char id;
+ unsigned char io_blks;
+ unsigned char io_addr;
+ unsigned char io_bus;
+ struct cis_ioblk *io;
+ unsigned char irqlevel;
+ unsigned char irq_flags;
+ unsigned irq_mask;
+ unsigned char memwins;
+ struct cis_memblk *mem;
+ unsigned char misc;
+};
+
+/*
+ * Structure holding all data retrieved from the
+ * CIS block on the card.
+ * The default configuration contains interface defaults
+ * not listed in each separate configuration.
+ */
+struct cis {
+ struct tuple_list *tlist;
+ char *manuf;
+ char *vers;
+ char *add_info1;
+ char *add_info2;
+ unsigned char maj_v, min_v;
+ unsigned char last_config;
+ unsigned char ccrs;
+ unsigned long reg_addr;
+ u_int manufacturer;
+ u_int product;
+ u_int prodext;
+ unsigned char func_id1, func_id2;
+ struct dev_mem attr_mem;
+ struct dev_mem common_mem;
+ struct cis_config *def_config;
+ struct cis_config *conf;
+ unsigned char *lan_nid;
+};
+
+#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 *xmalloc(int);
+void dump(unsigned char *, int);
+void dumpcis(struct cis *);
+void freecis(struct cis *);
+struct cis *readcis(int);
+
+char *tuple_name(unsigned char);
+u_int parse_num(int, u_char *, u_char **, int);
+
+int isdumpcisfile;
diff --git a/usr.sbin/pccard/pccardd/server.c b/usr.sbin/pccard/pccardd/server.c
new file mode 100644
index 0000000..8c6177e
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/server.c
@@ -0,0 +1,188 @@
+/*
+ * pccardd UNIX-domain socket interface
+ * Copyright (C) 1996 by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ *
+ * $Id: server.c,v 1.3 1999/02/07 08:02:44 kuriyama Exp $
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#include "cardd.h"
+
+static void
+cardnum(char *buf)
+{
+ int i = 0;
+ struct slot *sp;
+
+ for (sp = slots; sp; sp = sp->next)
+ i++;
+ if (i > MAXSLOT)
+ i = MAXSLOT;
+ sprintf(buf, "%2d", i);
+}
+
+static struct slot *
+find_slot(int slot)
+{
+ struct slot *sp;
+
+ /* Search the list until we find the slot or get to the end */
+ for (sp = slots; sp && sp->slot != slot; sp = sp->next)
+ continue;
+
+ return ( sp );
+}
+
+static void
+cardname(char *buf, int slot)
+{
+ struct slot *sp;
+ char *manuf, *vers, *drv, *stat;
+
+ /* Look for the slot */
+ if ( (sp = find_slot(slot)) == NULL)
+ return;
+
+ /* Fill in the information in the buff */
+ if (sp->cis) {
+
+ manuf = sp->cis->manuf;
+ vers = sp->cis->vers;
+ if (sp->config && sp->config->driver &&
+ sp->config->driver->name)
+ drv = sp->config->driver->name;
+ else
+ drv = "";
+ } else
+ manuf = vers = drv = "";
+
+ switch (sp->state) {
+ case empty:
+ stat = "0";
+ break;
+ case filled:
+ stat = "1";
+ break;
+ case inactive:
+ stat = "2";
+ break;
+ default:
+ stat = "9";
+ }
+ sprintf(buf, "%d~%s~%s~%s~%s", slot, manuf, vers, drv, stat);
+}
+
+static void
+cardpwr(int slot, int pwon)
+{
+ struct slot *sp;
+
+ /* Look for the slot */
+ if ( (sp = find_slot(slot)) == NULL)
+ return;
+
+ if (ioctl(sp->fd, PIOCSVIR, &pwon) < 0)
+ logerr("invaild arguments for cardpwr");
+}
+
+static int sock = 0;
+static int slen = 0;
+static struct sockaddr_un sun;
+
+void
+set_socket(int s)
+{
+ sock = s;
+}
+
+void
+stat_changed(struct slot *sp)
+{
+ int len;
+ char buf[512];
+
+ if (!slen)
+ return;
+
+ cardname(buf, sp->slot);
+ len = strlen(buf);
+ if (sendto(sock, buf, len, 0, (struct sockaddr *) & sun, slen) != len) {
+ logerr("sendto failed");
+ slen = 0;
+ }
+}
+
+void
+process_client(void)
+{
+ char buf[512], obuf[512];
+ int len;
+ int snum;
+
+ if (!sock)
+ return;
+ slen = sizeof(sun);
+ len = recvfrom(sock, buf, sizeof(buf),
+ 0, (struct sockaddr *)&sun, &slen);
+ if (len < 0)
+ logerr("recvfrom failed");
+ buf[len] = '\0';
+ obuf[0] = '\0';
+ switch (buf[0]) { /* Protocol implementation */
+ case 'S': /* How many slots? */
+ cardnum(obuf);
+ break;
+ case 'N': /* Card name request */
+ sscanf(buf + 1, "%d", &snum);
+ if (snum >= 0 && snum <= MAXSLOT)
+ cardname(obuf, snum);
+ else
+ logerr("Illegal slot requests for N command");
+ break;
+ case 'P': /* Virtual insertion request */
+ sscanf(buf + 1, "%d", &snum);
+ if (snum >= 0 && snum <= MAXSLOT) {
+ logmsg("slot %d: spring has come", snum);
+ cardpwr(snum, 1);
+ } else
+ logerr("Illegal slot requests for P command");
+ break;
+ case 'Q': /* Virtual removal request */
+ sscanf(buf + 1, "%d", &snum);
+ if (snum >= 0 && snum <= MAXSLOT) {
+ logmsg("slot %d: hibernation", snum);
+ cardpwr(snum, 0);
+ } else
+ logerr("Illegal slot requests for Q command");
+ break;
+ default:
+ logerr("Unknown control message from socket");
+ break;
+ }
+ len = strlen(obuf);
+ if (len) {
+ if (sendto(sock, obuf, len, 0, (struct sockaddr *)&sun, slen)
+ != len) {
+ logerr("sendto failed");
+ slen = 0;
+ }
+ } else if (sendto(sock, 0, 0, 0, (struct sockaddr *)&sun, slen)
+ != len) {
+ logerr("sendto failed");
+ slen = 0;
+ }
+}
diff --git a/usr.sbin/pccard/pccardd/util.c b/usr.sbin/pccard/pccardd/util.c
new file mode 100644
index 0000000..8f9a48d
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/util.c
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by:
+ * Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
+ * Nate Williams <nate@FreeBSD.org>
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#ifdef SYSINSTALL
+#include <dialog.h>
+#endif
+#include "cardd.h"
+
+static int do_log = 0;
+
+void
+log_setup(void)
+{
+#ifndef SYSINSTALL
+ do_log = 1;
+ openlog("pccardd", LOG_PID, LOG_DAEMON);
+#endif
+}
+
+void
+logmsg(const char *fmt, ...)
+{
+ va_list ap;
+ char s[256];
+
+ va_start(ap, fmt);
+ vsnprintf(s, 256, fmt, ap);
+
+ if (do_log)
+ syslog(LOG_ERR, "%s", s);
+ else {
+#ifdef SYSINSTALL
+ dialog_clear();
+ msgConfirm(s);
+#else
+ warnx("%s", s);
+#endif
+ }
+}
+
+void
+logerr(char *msg)
+{
+ if (do_log)
+ syslog(LOG_ERR, "%s: %m", msg);
+ else {
+#ifdef SYSINSTALL
+ dialog_clear();
+ msgConfirm(msg);
+#else
+ warn("%s", msg);
+#endif
+ }
+}
+
+/*
+ * Deliver last will and testament, and die.
+ */
+void
+die(char *msg)
+{
+ if (do_log)
+ syslog(LOG_CRIT, "fatal error: %s", msg);
+ else {
+#ifdef SYSINSTALL
+ char s[256];
+
+ sprintf(s, "cardd fatal error: %s\n", msg);
+ dialog_clear();
+ msgConfirm(s);
+#else
+ warnx("fatal error: %s", msg);
+#endif
+ }
+ closelog();
+ exit(1);
+}
+
+void *
+xmalloc(int sz)
+{
+ void *p;
+
+ p = malloc(sz);
+ if (p)
+ bzero(p, sz);
+ else
+ die("malloc failed");
+ return (p);
+}
+
+char *
+newstr(char *p)
+{
+ char *s;
+
+ s = strdup(p);
+ if (s == 0)
+ die("strdup failed");
+ return (s);
+}
+
+/*
+ * Find contiguous bit string (all set) of at
+ * least count number.
+ */
+int
+bit_fns(bitstr_t *nm, int nbits, int min, int count, int step)
+{
+ int i, j;
+ int found = 0;
+
+ for (i = min; i < nbits; i += step)
+ for (j = i, found = 0; j < nbits; j++)
+ if (bit_test(nm, j)) {
+ if (++found == count)
+ return i;
+ } else
+ break;
+ return (-1);
+}
+
+/*
+ * Allocate a block of memory and return the address.
+ */
+unsigned long
+alloc_memory(int size)
+{
+ int i;
+
+ i = bit_fns(mem_avail, MEMBLKS, 0, size / MEMUNIT + (size % MEMUNIT != 0), 1);
+ if (i < 0)
+ return (0);
+ bit_nclear(mem_avail, i, i + size / MEMUNIT + (size % MEMUNIT != 0) - 1);
+ return (BIT2MEM(i));
+}
+
+/*
+ * reset_slot - Power has been applied to the card.
+ * Now reset the card.
+ */
+void
+reset_slot(struct slot *sp)
+{
+ char c;
+ off_t offs;
+ struct mem_desc mem;
+ struct io_desc io;
+ int rw_flags;
+
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+#ifdef DEBUG
+ printf("Resetting card, writing 0x80 to offs 0x%x\n",
+ sp->cis->reg_addr);
+#endif
+ offs = sp->cis->reg_addr;
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(10 * 1000);
+ c = 0;
+ lseek(sp->fd, offs, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+
+ /* Reset all the memory and I/O windows. */
+ bzero((caddr_t) & mem, sizeof(mem));
+ bzero((caddr_t) & io, sizeof(io));
+ for (mem.window = 0; mem.window < NUM_MEM_WINDOWS; mem.window++)
+ ioctl(sp->fd, PIOCSMEM, &mem);
+ for (io.window = 0; io.window < NUM_IO_WINDOWS; io.window++)
+ ioctl(sp->fd, PIOCSIO, &io);
+}
+
+/*
+ * execute - Execute the command strings.
+ * For the current slot (if any) perform macro
+ * substitutions.
+ */
+void
+execute(struct cmd *cmdp, struct slot *sp)
+{
+ char cmd[1024];
+ char *p, *cp, *lp;
+
+ for (; cmdp; cmdp = cmdp->next) {
+ cp = cmd;
+ lp = cmdp->line;
+ if (*lp == 0)
+ continue;
+ while ((p = strchr(lp, '$')) != 0) {
+ /* copy over preceding string. */
+ while (lp != p)
+ *cp++ = *lp++;
+ /* stringify ethernet address and place here. */
+ if (strncmp(p, "$ether", 6) == 0) {
+ sprintf(cp, "%x:%x:%x:%x:%x:%x",
+ sp->eaddr[0],
+ sp->eaddr[1],
+ sp->eaddr[2],
+ sp->eaddr[3],
+ sp->eaddr[4],
+ sp->eaddr[5]);
+ while (*++cp)
+ continue;
+ lp += 6;
+ } else
+ /* replace device name */
+ if (strncmp(p, "$device", 7) == 0) {
+ sprintf(cp, "%s%d",
+ sp->config->driver->kernel,
+ sp->config->driver->unit);
+ while (*cp)
+ cp++;
+ lp += 7;
+ } else
+ /* Copy the `$' and rescan. */
+ *cp++ = *lp++;
+ }
+ /* No more replacements. Copy rest of string. */
+ while ((*cp++ = *lp++) != 0)
+ continue;
+#ifdef DEBUG
+ fprintf(stderr, "Executing [%s]\n", cmd);
+#endif
+ system(cmd);
+ }
+}
diff --git a/usr.sbin/pciconf/Makefile b/usr.sbin/pciconf/Makefile
new file mode 100644
index 0000000..d892a12
--- /dev/null
+++ b/usr.sbin/pciconf/Makefile
@@ -0,0 +1,9 @@
+# $ANA: Makefile,v 1.1.1.1 1996/09/25 21:12:57 wollman Exp $
+# $FreeBSD$
+
+PROG= pciconf
+MAN= pciconf.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
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..ea41bba
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.8
@@ -0,0 +1,222 @@
+.\" 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 February 7, 1997
+.Dt PCICONF 8
+.Os
+.Sh NAME
+.Nm pciconf
+.Nd diagnostic utility for the PCI bus
+.Sh SYNOPSIS
+.Nm
+.Fl l Op Fl v
+.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:4:0: class=0x010000 card=0x00000000 chip=0x000f1000 rev=0x01 hdr=0x00
+bar0@pci0:5:0: class=0x000100 card=0x00000000 chip=0x88c15333 rev=0x00 hdr=0x00
+none0@pci0:6:0: class=0x020000 card=0x00000000 chip=0x802910ec rev=0x00 hdr=0x00
+.Ed
+.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
+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.
+It is 0 for most current (2.0)
+.Tn PCI
+cards, but is supposed to be loaded with a unique card identification code
+in newly developed
+.Tn PCI
+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 are 0 for all devices except
+.Tn PCI
+to
+.Tn PCI
+bridges, and 1 for such bridge chips.
+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
+All invocations of
+.Nm
+except for
+.Fl l
+require a
+.Ar selector
+of the form
+.Li pci Ns Va bus Ns \&: Ns Va device
+(optionally followed by
+.Li \&: Ns Va function ) .
+A final colon may be appended and
+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 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..0ea232a
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.c
@@ -0,0 +1,544 @@
+/*
+ * 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 <err.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"
+
+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_devs(int vendors);
+static void list_verbose(struct pci_conf *p);
+static char *guess_class(struct pci_conf *p);
+static 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()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: pciconf -l [-v]",
+ " 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, verbose;
+ int byte, isshort;
+
+ listmode = readmode = writemode = attachedmode = verbose = byte = isshort = 0;
+
+ while ((c = getopt(argc, argv, "alrwbhv")) != -1) {
+ switch(c) {
+ case 'a':
+ attachedmode = 1;
+ break;
+
+ case 'l':
+ listmode = 1;
+ break;
+
+ case 'r':
+ readmode = 1;
+ break;
+
+ case 'w':
+ writemode = 1;
+ break;
+
+ case 'b':
+ byte = 1;
+ break;
+
+ case 'h':
+ isshort = 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);
+ } 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 fd;
+ struct pci_conf_io pc;
+ struct pci_conf conf[255], *p;
+ int none_count = 0;
+
+ if (verbose)
+ load_vendors();
+
+ fd = open(_PATH_DEVPCI, 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:\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_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);
+ }
+ } while (pc.status == PCI_GETCONF_MORE_DEVS);
+
+ close(fd);
+}
+
+static void
+list_verbose(struct pci_conf *p)
+{
+ struct pci_vendor_info *vi;
+ struct pci_device_info *di;
+ 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;
+ 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_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_DISPLAY, -1, "display"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"},
+ {PCIC_MULTIMEDIA, -1, "multimedia"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"},
+ {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_OTHER, "PCI-unknown"},
+ {PCIC_SIMPLECOMM, -1, "simple comms"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"},
+ {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_INPUTDEV, -1, "input device"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"},
+ {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"},
+ {0, 0, NULL}
+};
+
+static 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 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)
+{
+ char *dbf;
+ FILE *db;
+ struct pci_vendor_info *cv;
+ struct pci_device_info *cd;
+ char buf[100], str[100];
+ 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;
+
+ /* Check for vendor entry */
+ if ((buf[0] != '\t') && (sscanf(buf, "%04x\t%[^\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\t%[^\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);
+}
+
+
+static struct pcisel
+getsel(const char *str)
+{
+ char *ep = strchr(str, '@');
+ char *epbase;
+ struct pcisel sel;
+
+ if (ep == NULL)
+ ep = (char *)str;
+ else
+ ep++;
+
+ epbase = ep;
+
+ if (strncmp(ep, "pci", 3) == 0) {
+ ep += 3;
+ sel.pc_bus = strtoul(ep, &ep, 0);
+ if (!ep || *ep++ != ':')
+ errx(1, "cannot parse selector %s", str);
+ sel.pc_dev = strtoul(ep, &ep, 0);
+ if (!ep || *ep != ':') {
+ sel.pc_func = 0;
+ } else {
+ ep++;
+ sel.pc_func = strtoul(ep, &ep, 0);
+ }
+ if (*ep == ':')
+ ep++;
+ }
+ 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)
+{
+ 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)");
+
+ printf("%0*x", width*2, pi.pi_data);
+}
+
+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/pcvt/Makefile b/usr.sbin/pcvt/Makefile
new file mode 100644
index 0000000..0dbdf05
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+SUBDIR= keycap cursor fontedit fonts kcon loadfont scon \
+ userkeys vttest ispcvt
+SUBDIR+= vgaio kbdio
+SUBDIR+= demo
+SUBDIR+= Misc
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Makefile.inc b/usr.sbin/pcvt/Makefile.inc
new file mode 100644
index 0000000..7899d9c
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+FONTDIR = /usr/share/misc/pcvtfonts
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/pcvt/Misc/Doc/Acknowledgements b/usr.sbin/pcvt/Misc/Doc/Acknowledgements
new file mode 100644
index 0000000..327f23d
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Acknowledgements
@@ -0,0 +1,111 @@
+Thank You, (in the order of appearance)
+
+
+ Lynne and Bill Jolitz
+
+ for your work on 386BSD and making all this possible !
+
+
+ Holger Veit (veit@du9ds3.uni-duisburg.de)
+
+ for the permission to use a modified version of his keyboard
+ driver and utilities for keyboard remapping / multinational
+ keyboard support.
+
+
+ Per Lindberg
+
+ for the extremely helpful vt100 terminal testprogram found
+ in the directory vttest.
+
+
+ John Birchfield
+
+ for the ncsa telnet package, which contains a vt100 emulation
+ and which was very helpful in studying some concepts.
+
+
+ Ralf Friedl (friedl@informatik.uni-kl.de)
+
+ for making his implementation of multi-sceens for the net-2
+ distribution available. i looked at this and took some ideas
+ and lines from his code.
+
+
+ Bruce Evans (bde@runx.oz.au)
+
+ for contributing some bugfixes and a complete termcap entry
+
+
+ Brian H. Dunford-Shore (brian@morpheus.wustl.edu)
+
+ for contributing most of the EGA/VGA screen switching code
+ and being a fast, reliable and responsive co-author. This
+ driver would not be what it is without Brian, Thank You !
+
+
+ Frank da Cruz (fdc@columbia.edu)
+
+ for my famous datacomm program and for giving the permission
+ to redistribute files from the msdos kermit distribution
+ located in the directory support/demo.
+
+
+ Joerg Wunsch (joerg_wunsch@uriah.sax.de)
+
+ for contributing precise bugreports and -fixes, the 8x10
+ EGA/VGA fonts and the color palette ioctls and for being
+ a very responsive contributor of various ideas.
+ Joerg wrote pcvt's interface to XFree86 1.2 and 1.3 and
+ the SYSV/syscons - like interface to XFree86 2.0.
+ There would be no support for X without Joerg's work!
+ Thank you very much Joerg, i enjoy it !!! :-)
+
+
+ Scott Turner (scotty@gagetalker.com)
+
+ for contributing code to change the winsize structure, many
+ discussions on the keyboard code and fine-tuning the driver
+
+
+ Gordon L. Burditt (gordon@sneaky.lonestar.org)
+
+ for the nicest and most complete bugreports i ever got
+
+
+ Theo Deraadt (deraadt@fsa.ca)
+
+ for pushing me forward. There would be no 3.00 release
+ if Theo didn't asked for features ... ;-)
+
+
+ Onno van der Linden (c/o frank@fwi.uva.nl)
+
+ for writing the 132 column support for the Cirrus
+ chipsets although he had no time to do it ... :-)
+
+
+ Wolfram Solfrank, Ingo Koenig
+
+ for putting some data onto tape (and handling and shipping
+ in the case of Wolfgang) to provide me with some latest
+ sources because i still have no ip-connectivity ....
+
+
+ Michael Havemester (tik@abqhh.hanse.de)
+
+ for giving me a chance to stay up to date with NetBSD-
+ current, for programming the initial version of the fast
+ scrolling code and for the keyboard fifo code!
+
+
+ Charles Hannum (mycroft@gnu.ai.mit.edu)
+
+ for getting bored by a slow-scrolling video driver and
+ for leaving me some bugs to fix ;-)
+
+
+ The NetBSD and FreeBSD teams
+
+ for giving me something to play, work and learn with !
+ There would be nothing to write a driver for without you !!!
diff --git a/usr.sbin/pcvt/Misc/Doc/Bibliography b/usr.sbin/pcvt/Misc/Doc/Bibliography
new file mode 100644
index 0000000..2cce039
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Bibliography
@@ -0,0 +1,189 @@
+
+VGA BOOKS
+---------
+
+ Richard Wilton, "Programmers Guide to PC & PS/2 Video Systems",
+ Microsoft Press 1987
+
+
+ Richard F. Ferraro, "Programmers Guide to the EGA and VGA Cards",
+ Second Edition, Addison-Wesley 1990
+
+
+ Richard F. Ferraro, "Programmers Guide to the EGA and VGA Cards",
+ Third Edition, Addison-Wesley 1994
+
+
+ Matthias Uphoff, "Die Programmierung der EGA/VGA Grafikkarte",
+ Addison Wesley 1992
+
+
+ Bradley Dyck Kliewer, "EGA/VGA A Programmers Reference Guide",
+ McGraw Hill, 2nd Edition 1990
+
+
+UNIX AND UNIX DEVICE DRIVERS
+----------------------------
+
+ Bell Telephone Laboratories, Inc. "UNIX Programmer's Manual, Seventh
+ Edition, Volume 2". Revised and Expanded Version.
+ Holt, Rinehart and Winston 1983
+
+
+ George Pajari, "Writing Unix Device Drivers"
+ Addison Wesley 1992
+
+
+ Janet I. Egan and Thomas J. Teixeira, "Writing a UNIX Device Driver"
+ John Wiley & Sons 1988
+
+
+ Janet I. Egan and Thomas J. Teixeira, "Writing a UNIX Device Driver"
+ Second Edition. John Wiley & Sons 1992
+
+
+ Leffler, McKusick, Karels, Quarterman, "The Design and Implementation
+ of the 4.3BSD UNIX Operating System"
+ Addison Wesley 1988, corrected Reprint 1989
+
+
+ Leffler, McKusick, "The Design and Implementation of the 4.3BSD UNIX
+ Operating System, Answer Book"
+ Addison Wesley 1991
+
+
+ Maurice J. Bach, "The Design of the UNIX Operating System"
+ Prentice-Hall 1986
+
+
+ Sun Microsystems Inc., "Writing Device Drivers"
+ Part No. 800-3851-10, Revision A of 27 March 1990
+
+
+ Hewlett-Packard Company, "HP-UX Driver Development Guide",
+ Part No. 98577-90013, First Edition 07/91
+
+
+ W. Richard Stevens, "Advanced Programming in the UNIX Environment",
+ Addison Wesley 1992
+
+
+ Phillip M. Adams, Clovis L. Tondo, "Writing Unix Device Drivers in C",
+ Prentice Hall 1993
+
+
+ Berny Goodheart, James Cox, "The Magic Garden Explained",
+ Prentice Hall 1994
+
+
+ Peter Kettle, Steve Statler, "Writing Device Drivers for SCO Unix"
+ Addison Wesley 1993
+
+
+TERMINAL MANUALS
+----------------
+
+ Digital Equipment, VT100 Users Manual, 2nd ed. 1979
+ Digital Equipment, VT132 Users Manual
+ Digital Equipment, VT220 Programmers Reference Manual, 2nd ed. 1984
+ Digital Equipment Corporation
+
+
+ Hewlett Packard, 700/92 Technical Reference Manual (ANSI Operation)
+ Hewlett Packard, 2392a Users Manual (ANSI Operation)
+ Hewlett-Packard Company
+
+
+ Walker Richer & Quinn, Inc., "Reflection 2 and Reflection 4 Technical
+ Reference Manual", Version 4.2
+ Walker, Richer & Quinn, Seattle, August 1992
+
+
+IBM PC HARDWARE/FIRMWARE
+------------------------
+
+ Frank van Gilluwe, "The Undocumented PC",
+ Addison Wesley, First Edition May 1994
+
+
+ IBM Corporation, "Technical Reference Personal Computer AT",
+ Part No. 6280070, Form No. S229-9611-00, First Edition 1985
+
+
+ Phoenix Technologies Ltd., "System BIOS for IBM PC/XT/AT Computers
+ and Compatibles", Addison Wesley, Fourth Printing June 1990
+
+
+ Phoenix Technologies Ltd., "System BIOS for IBM PCs, Compatibles,
+ and EISA Computers", Second Edition
+ Addison Wesley, First Printing May 1991
+
+
+ American Megatrends, Inc., "Hi-Flex ISA and EISA AMIBIOS Technical
+ Reference", American Megatrends, Inc. 1992 (9/25/92)
+
+
+ Thom Hogan, "The Programmers PC Sourcebook", 2nd Edition
+ Microsoft Press, 1991
+
+
+TERMCAP/TERMINFO
+----------------
+
+ John Strang, Linda Mui and Tim O'Reilly, "Termcap and Terminfo",
+ O'Reilly & Associates, Inc. , April 1991
+
+
+ Richard M. Stallman, "Termcap - The Termcap Library and Data Base",
+ Free Software Foundation, Second Edition November 1988
+
+
+
+DATABOOKS/DATASHEETS
+--------------------
+
+ Intel Corporation, "Microsystem Components Handbook Volume II",
+ Intel Corporation, 1984
+
+
+ Intel Corporation, "Peripheral Design Handbook",
+ Intel Corporation, 1980
+
+
+ Tseng Labs, Inc. "ET4000 Graphics Controller Data Book", 1990
+
+
+ Western Digital Corporation, "WD90C11, WD90C11A (PVGA1C) Enhanced
+ VGA Controller", Western Digital 9/18/91
+
+
+ Western Digital Corporation, "WD90C00 Enhanced VGA Controller",
+ Western Digital 1/14/91
+
+
+ Western Digital Corporation, "WD90C00 Interface Guide",
+ Western Digital 1/10/91
+
+
+ Western Digital Corporation, "VGA Register Based Programmers Manual",
+ Western Digital 1/30/91
+
+
+ Western Digital Corporation, "VGA BIOS Programmers Manual",
+ Western Digital 1/10/91
+
+
+ Cirrus Logic, "True Color VGA Family - CL-GD542X", Technical Ref.
+ Manual, Cirrus Logic, Inc. April 1993
+
+
+ S3 Inc, "86C801/86C805 GUI Accelerators"
+ S3 Incorporated, September 1992
+
+
+ S3 Inc, "86C928 GUI Accelerator"
+ S3 Incorporated, July 1993
+
+
+ Trident Microsystems Inc., "TVGA9000i Technical Reference Manual",
+ Trident Microsystems, Inc. (c) 1992, March 1993, Rev 1
diff --git a/usr.sbin/pcvt/Misc/Doc/CharGen b/usr.sbin/pcvt/Misc/Doc/CharGen
new file mode 100644
index 0000000..c047bfa
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/CharGen
@@ -0,0 +1,149 @@
+Character Generator description (before it gets lost ..)
+--------------------------------------------------------------------------------
+
+The lower character generator is the default IBM character set II.
+
+The description of the higher character generator follows below. The
+character names are taken from the "Postscript Language Reference
+Manual", 2nd Edition, Fourth printing July 1991, pp 596.
+
+NOTE: The hex values here are NOT the values the character is identified
+ by in the emulator. The "physical" (vt220 character) to "logical"
+ (this table, character generator) conversion is done in the character
+ output routine by using the tables from file pcvt_tbl.h.
+
+The order of the characters is not regular and was largely influenced by
+the status of my brain while pixel-placing characters ....
+
+HEX What
+--- -------------------------------------------------
+00 Control code display for
+. control characters in the
+1f range 0x00 - 0x1f
+
+20 Control code display for
+. control characters in the
+3f range 0x80 - 0x9f
+
+HEX What
+--- -------------------------------------------------
+40 rho
+41 psi
+42 partialdiff
+43 lambda
+44 iota
+45 eta
+46 epsilon
+47 chi
+48 logicaland
+49 logicalor
+4a union
+4b propersuperset
+4c propersubset
+4d gamma
+4e Xi
+4f Psi
+
+HEX What
+--- -------------------------------------------------
+50 Pi
+51 arrowdblright
+52 arrowdblboth
+53 Lambda
+54 Theta
+55 congruent
+56 gradient
+57 Delta
+58 proportional
+59 therefore
+5a integral
+5b fraction
+5c (inverted fraction ????)
+5d angle
+5e (inverted angle ????)
+5f braceleftmid
+
+HEX What
+--- -------------------------------------------------
+60 bracerightmid
+61 bracelefttp
+62 braceleftbt
+63 bracerighttp
+64 bracerightbt
+65 radical
+66 omega
+67 (Yen ??)
+68 xi
+69 yacute
+6a thorn
+6b eth
+6c Thorn
+6d Yacute
+6e multiply
+6f Eth
+
+HEX What
+--- -------------------------------------------------
+70 threequarters
+71 Cedillasmall
+72 Acutesmall
+73 emdash
+74 registered
+75 endash
+76 logicalnot
+77 dieresis
+78 notequal
+79 scan 9
+7a scan 7
+7b scan 5
+7c scan 3
+7d scan 1
+7e upsilon
+7f emptyset
+
+HEX What
+--- -------------------------------------------------
+80 oe
+81 Otilde
+82 atilde
+83 Ydieresis
+84 Ucircumflex
+85 Uacute
+86 Ugrave
+87 Oslash
+88 OE
+89 Otilde
+8a Ocircumflex
+8b Oacute
+8c Ograve
+8d Idieresis
+8e Icircumflex
+8f Iacute
+
+HEX What
+--- -------------------------------------------------
+90 Igrave
+91 Edieresis
+92 Ecircumflex
+93 Egrave
+94 Atilde
+95 Acircumflex
+96 Aacute
+97 Agrave
+98 onesuperior
+99 (small black rectangle)
+9a zeta
+9b threesuperior
+9c copyright
+9d currency
+9e kappa
+9f (inverted question mark)
+
+HEX What
+--- -------------------------------------------------
+a0 the remaining positions
+. are used for the vt220
+ff downloadable characterset
+
+-------------------------------------------------------------------------------
+ (phoooo ..)
diff --git a/usr.sbin/pcvt/Misc/Doc/Charsets b/usr.sbin/pcvt/Misc/Doc/Charsets
new file mode 100644
index 0000000..b30dd8a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Charsets
@@ -0,0 +1,99 @@
+
+I. Character Set Selection on VT220 Terminals
+==================================================
+
+
+name C0 GL C1 GR
+ +---+ +-------------------+ +---+ +-------------------+
+ |00h| | | |80h| | |
+range | | | | 20h .. 7fh | | | | | a0h .. ffh |
+ |1fh| | | |9fh| | |
+ +---+ +-------------------+ +---+ +-------------------+
+length 32 96 32 96
+
+ /\
+SECOND /||\ "lock"- or "single"-shift one set of G0, G1, G2 or
+STEP || G3 into one of the "displayable" charactersets GL
+ || or GR. (escape) sequences are: SI, SO, ESC ~, ESC n,
+ || ESC }, ESC o, ESC |, ESC N and ESC O.
+
+ +----+ +----+ +----+ +----+
+name | G0 | | G1 | | G2 | | G3 |
+length |(96)| |(96)| |(96)| |(96)|
+ +----+ +----+ +----+ +----+
+
+ /\
+ /||\ designate a hard or a soft character set as
+ || one of G0, G1, G2 or G3, used escape sequences
+FIRST || are, ESC ( X, ESC ) X, ESC * X and ESC + X - where X is
+STEP || B for ascii, < for supplemental, 0 for special, A for
+ || british, 4 for dutch, C and 5 for finnish etc. etc. ...
+ ||
+
+ +-----+ +------------+ +--------+ +-----------+ +------------+
+name |ascii| |supplemental| |special | | national | |downloadable|
+ | | | graphics | |graphics| |replacement| |characterset|
+length | (96)| | (96)| | (96)| | (96)| | (96)|
+ +-----+ +------------+ +--------+ +-----------+ +------------+
+
+ \-------\ /----------/ norway/danish
+ | dutch
+ together, this is also finnish
+ referred to as the french
+ multinational character french canada
+ set (power on default german
+ on a dec vt220) italian
+ spanish
+ swedish
+ swiss
+
+
+II. Emulating Character Set Selection
+=========================================
+
+MDA/HCG/CGA:
+
+ just a partial emulation is done, because these boards don't allow
+ downloadable charactersets. some characters simply don't display
+ because they are not in the characterset roms on the video board.
+
+ if you want to change the mapping, have a look at the default tables
+ in pcvt_tbl.h.
+
+EGA/VGA:
+
+ these cards have provisions for downloadable charactersets and so
+ many vt220/vt320 charactersets are fully supported:
+
+ - US Ascii
+ - DEC Supplemental
+ - DEC Special Graphic
+ - DEC Technical
+ - ISO Latin-1
+ - Downloadable
+
+ when the emulator is started, it behaves initially similar to
+ the MDA/CGA/HCG method described above.
+
+ when a second (special) characterset for a given screen resolution
+ is loaded via the "loadfont" utility, the emulator detects this fact
+ and uses from then on extended tables to access the second charset
+ as an upper half 512 characterset in terms of ega/vga speak.
+
+ from this time on, vt220 downloadable fonts are usable.
+
+ the organization of this extended characterset is as follows:
+
+ 0x00 ... 0x1f C0 display function fonts
+ 0x20 ... 0x3f C1 display function fonts
+ 0x40 ... 0x9f fonts for DEC Supplemental, DEC Special
+ Graphic, DEC Technical and ISO Latin-1
+ 0xa0 ... 0xff VT220 Downloadable Font
+
+ the mapping between vt220 charactersets and the charactersets inside
+ the ega/vga is done by tables found in the file pcvt_tbl.h.
+
+ there is a MSDOS fonteditor called "EVAFONT" available on the
+ SIMTEL-20 archive, which i used to edit the character sets in the
+ distribution.
+
diff --git a/usr.sbin/pcvt/Misc/Doc/EscapeSequences b/usr.sbin/pcvt/Misc/Doc/EscapeSequences
new file mode 100644
index 0000000..8e0e2c7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/EscapeSequences
@@ -0,0 +1,268 @@
+ Control Codes and Escape Sequences supported by pcvt
+===============================================================================
+
+CONTROL CODES
+-------------------------------------------------------------------------------
+
+ NUL (0x00) ignored
+ SOH (0x01) ignored
+ STX (0x02) ignored
+ ETX (0x03) ignored
+ EOT (0x04) ignored
+ ENQ (0x05) ignored
+ ACK (0x06) ignored
+
+ BEL (0x07) beep
+
+ BS (0x08) move one character position to the left
+ until at left margin
+
+ HT (0x09) move to next tab stop
+
+ LF (0x0a) move to next line, same column
+ VT (0x0b) move to next line, same column
+ FF (0x0c) move to next line, same column
+
+ CR (0x0d) move to left margin on current column
+
+ SO (0x0e) invoke character set G1 into GL
+ SI (0x0f) invoke character set G0 into GL
+
+ DLE (0x10) ignored
+ DC1 (0x11) ignored
+ DC2 (0x12) ignored
+ DC3 (0x13) ignored
+ DC4 (0x14) ignored
+ NAK (0x15) ignored
+ SYN (0x16) ignored
+ ETB (0x17) ignored
+
+ CAN (0x18) abort current escape sequence
+
+ EM (0x19) ignored
+
+ SUB (0x1a) abort current escape sequence
+
+ ESC (0x1b) start of escape sequence
+
+ FS (0x1c) ignored
+ GS (0x1d) ignored
+ RS (0x1e) ignored
+ US (0x1f) ignored
+
+ VT220 control codes in the range 0x80 .. 0x9f are completely ignored,
+ but displayed as C1 display controls.
+
+
+ESCAPE SEQUENCES DIGITAL EQUIPMENT
+-------------------------------------------------------------------------------
+
+(ni) = not implemented yet, all hooks available inside emulator!
+<p> = numeric parameter
+
+ ESC space F select 7-bit c1 control transmission (ni)
+ ESC space G select 8-bit c1 control transmission (ni)
+
+ ESC # 3 double height top half (ni)
+ ESC # 4 double height bottom half (ni)
+ ESC # 5 single width single height (ni)
+ ESC # 6 double width single height (ni)
+
+ ESC # 8 fill screen with 'E's
+
+ ESC 7 save cursor
+
+ ESC 8 restore cursor
+
+ ESC = keypad application mode
+
+ ESC > keypad numeric mode
+
+ ESC D index
+
+ ESC E next line
+
+ ESC H set tab at cur col
+
+ ESC M reverse index
+
+ ESC N single shift G2
+
+ ESC O single shift G3
+
+ ESC Z who are you
+
+ ESC d Only available if PCVT_SETCOLOR was defined when
+ compiling the kernel, allows to set custom color table
+ for more info, see pcvt_out.c ...
+
+ ESC c power up reset
+
+ ESC n Lock Shift G2 -> GL
+
+ ESC o Lock Shift G3 -> GL
+
+ ESC } Lock Shift G2 -> GR
+
+ ESC | Lock Shift G3 -> GR
+
+ ESC ~ Lock Shift G1 -> GR
+
+ ESC [ ? <p> h set dec private modes
+ ESC [ ? <p> l reset dec private modes
+ 1 CKM - cursor key mode
+ 6 OM - origin mode
+ 7 AWM - auto wrap mode
+
+ ESC [ <p> ' z DECELR - Enable Locator Report (ni)
+
+ ESC [ <p> ' { DECSLE - Select type of locator events (ni)
+
+ ESC [ ? n Terminal Reports
+
+ ESC [ ? K selective erase in line
+
+ ESC [ ? J selective erase in display
+
+ ESC [ <p> @ insert char(s)
+
+ ESC [ <p> A cursor up
+
+ ESC [ <p> B cursor down
+
+ ESC [ <p> C cursor forward
+
+ ESC [ <p> D cursor backward
+
+ ESC [ <p> H cursor direct cursor addressing
+
+ ESC [ <p> J erase screen
+
+ ESC [ <p> K erase line
+
+ ESC [ <p> L insert line
+
+ ESC [ <p> M delete line
+
+ ESC [ <p> P delete char
+
+ ESC [ <p> S scroll up
+
+ ESC [ <p> T scroll down
+
+ ESC [ <p> X erase character
+
+ ESC [ <p> c device attributes
+
+ ESC [ <p> f direct cursor addressing
+
+ ESC [ <p> g clear tabs
+
+ ESC [ <p> h set mode
+ ESC [ <p> l reset mode
+ 4 IRM - insert replacement mode
+ 20 LNM - line feed / newline mode
+
+ ESC [ <p> i media copy (ni)
+
+ ESC [ <p> m select graphic rendition
+ 0 reset to normal attributes
+ 1 bold
+ 4 underline
+ 5 blinking
+ 7 reverse
+ 22 bold off
+ 24 underline off
+ 25 blinking off
+ 27 reverse off
+ 30-37 foreground colors (on color display)
+ 40-47 background colors (on color display)
+
+ ESC [ <p> n device status report
+
+ ESC [ <p> r set scrolling region
+
+ ESC [ <p> x request / report terminal parameters
+
+ ESC [ <p> y invoke selftests (ni)
+
+
+ ESC [ x request/report parameters
+ ESC [ y invoke seftest(s)
+
+ ESC [ " q SCA
+ ESC [ ! p SCA
+
+ ESC ( <p> designate G0
+ ESC ) <p> designate G1
+ ESC * <p> designate G2
+ ESC + <p> designate G3
+ ESC - <p> designate G1 (96)
+ ESC . <p> designate G2 (96)
+ ESC / <p> designate G3 (96)
+ A British or ISO-Latin-1
+ B USASCII
+ C Finnish
+ 5 Finnish
+ E Norwegian/Danish
+ 6 Norwegian/Danish
+ H Swedish
+ 7 Swedish
+ K German
+ Q French Canadien
+ R French
+ Y Italian
+ Z Spanish
+ 0 special graphics
+ 1 alternate ROM
+ 2 alt ROM, spec graphics
+ 3 HP Roman 8, upper 128 chars*/
+ 4 Dutch
+ < DEC Supplemental
+ = Swiss
+ > DEC Technical
+
+
+ESCAPE SEQUENCES HEWLETT-PACKARD
+-------------------------------------------------------------------------------
+
+ESC&f<attr>a<key>k<llen>d<slen>L<label><string> set function key label
+
+ attr = attribute, 0 - normal (not implemented)
+ 1 - local only (not implemented)
+ 2 - transmit only (not implemented)
+
+ key = function key number, range 1 .. 8
+
+ llen = label string length
+
+ slen = string string length
+
+ label = label data (up to 16 chars per label)
+
+ string = string to send data (up to 80 chars per label)
+ (not implemented)
+
+EXAMPLE:
+========
+
+ ESC&f0a1k16d1LFUNCTION KEY 0
+
+ sets function key label 1 to "FUNCTION KEY ".
+ should program fkey 1 to emit "0" on keypress
+
+
+ESC&j<parm> function key operations
+
+ parm = '@' remove the function key labels from screen
+
+ parm = 'A' display the modes set of function key labels
+
+ parm = 'B' enable & display user function key labels
+
+ parm = 'C' clear message & restore the current key labels
+
+ parm = 'R' enable usr/sys and menu and label modes
+
+ parm = 'S' disable usr/sys and menu and label modes
+
diff --git a/usr.sbin/pcvt/Misc/Doc/Keyboard.HP b/usr.sbin/pcvt/Misc/Doc/Keyboard.HP
new file mode 100644
index 0000000..06ad5b7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Keyboard.HP
@@ -0,0 +1,286 @@
+================================================================================
+# #
+# Function key mapping for the "more HP" - like layout #
+# #
+================================================================================
+
+
+I. Function Key Map
+========================
+
+
+PC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 |
+Key
+ _________________________________________________________________________
+UNSHF| 132 | Soft| | | | 7/8 | Dspl| Auto| SCRN| SCRN| SCRN| SCRN|
+SYS | Cols|Reset| | | Beep| Bit | Func| Wrap| 0 | 1 | 2 | 3 |
+LABEL|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+UNSHF| | | | | | | | | SCRN| SCRN| SCRN| SCRN|
+USER | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | 0 | 1 | 2 | 3 |
+LABEL|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ | | | | | | | | | Fkey| U/S |VT/HP| Next|
+ALT | F14 | HELP| DO | F17 | F18 | F19 | F20 | F21 |Label|Label| Mode| SCRN|
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ | User| User| User| User| User| User| User| User| | | | |
+SHIFT| F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | | | | |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ALT- | User| User| User| User| User| User| User| User| | | | |
+SHIFT| F14 | F15 | F16 | F17 | F18 | F19 | F20 | F21 | | | | |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+CTRL-| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN|
+ALT | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+
+PC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 |
+Key
+
+
+
+II. Special Keys Used To Change The General Runtime Functionality
+=====================================================================
+
+
+ To be consistent with XFfree86 2.0 Virtual Screen switching, it is
+ now also possible to switch screens by using:
+
+ CTRL - ALT - Fx , where x can be 1 ... No of screens compiled,
+ see the definition of PCVT_NSCREENS !
+
+
+ Virtual Screen/Terminal switching
+ ---------------------------------
+
+ F9 or CTRL-ALT-F1 switches to screen 0
+ F10 or CTRL-ALT-F2 switches to screen 1
+ F11 or CTRL-ALT-F3 switches to screen 2
+ F12 or CTRL-ALT-F4 switches to screen 3
+ CTRL-ALT-F5 switches to screen 4
+ CTRL-ALT-F6 switches to screen 5
+ CTRL-ALT-F7 switches to screen 6
+ CTRL-ALT-F8 switches to screen 7
+ CTRL-ALT-F9 switches to screen 8
+ CTRL-ALT-F10 switches to screen 9
+ CTRL-ALT-F11 switches to screen 10
+ CTRL-ALT-F12 switches to screen 11
+
+ (see also ALT-F12 below)
+
+
+ ALT-F9 Function key labels ON / OFF
+ ------------------------------------
+
+ this key is only operational, when in HP/VT mode, see F11
+
+ ALT-F9 toggles between function key labels displayed or not.
+
+ ON: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+ OFF: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ no function key labels displayed
+ no row/col display
+ no status/load avg line
+
+ applicable escape sequences:
+
+ switch OFF:
+ ESC & j @ remove labels from screen
+
+ switch ON:
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ ALT-F10 User / System Function key labels
+ -----------------------------------------
+
+ this key is only operational, when in HP/VT mode, see ALT-F11
+
+ ALT-F10 toggles between the display of user or system
+ function key labels
+
+ this key is only active if labels are toggled on via
+ the ALT-F9 function key
+
+ screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+
+ applicable escape sequences: (see above)
+
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ ALT-F11 Toggle between pure VT and HP-VT Emulation
+ --------------------------------------------------
+
+ This key switches between pure vt100/vt220 mode and
+ vt100/vt220 with hp-fkey-labels mode of operation.
+ this is not dependent of any screen resolution the
+ virtual consoles are in.
+
+ initially, after power on, all virtual screens are in
+ the pure vt-mode:
+
+ - the emulator does not execute any hp escape sequences
+ - the function key label lines are switched off
+ - no load average or status line is displayed
+ - no row / column counter is displayed
+ - no window number is displayed
+ - the full screen 25x80, 28x80 or 50x80 is usable
+ - ALT-F9 and ALT-F10 have no function
+
+ when toggled to the vt/hp mix of operation, the following
+ changes take place:
+
+ - the emulator executes the supported hp-esc sequences
+ - the function key labels are displayable depending
+ on the state of ALT-F9/F10
+ - the load average is displayed
+ - the row / column counters are displayed
+ - the window number is displayed in the lower right corner
+ - in any screen resolution, the last 3 lines are lost,
+ so one has a 22x80, 25x80 and 47x80 resolution
+ - F9 and F10 are operational.
+
+ when switching modes by means of ALT-F11, the following
+ changes to the current environment, the emulator may
+ be in, apply:
+
+ - the screen is cleared
+ - the cursor moves to the home position
+ - the scrolling region is reset to default
+
+ (this functionality is available via an ioctl)
+
+ ALT-F12 Cycle current screen
+ ----------------------------
+
+ This key cycles through the display of the video
+ screens. on startup, screen 0 is displayed; with every
+ keypress of F12, the next screen is displayed, wrapping
+ from the maximum screen number back to screen 0.
+
+ (this functionality is available via an ioctl)
+
+
+III. Special Keys used to change the Runtime Functionality of a Page
+=======================================================================
+
+ when in mixed HP/VT mode, one has two types of function key labels
+ on screen, user function keys and system function keys.
+
+ this functionality is NOT available in pure VT220 mode !
+
+ the user function keys emit the below mentioned VT220 function-
+ key sequences. the labels can be reprogrammed by use of escape
+ sequences.
+
+ in system function key mode, several pre-programmed functions inside
+ the emulator can be toggled, currently implemented are:
+
+ - F1, if a chipset is detected for which 132 operation is supported,
+ F1 toggles between 80 columns and 132 columns.
+ - F2, does a soft reset of the emulator code
+ - F3, -/-
+ - F4, -/-
+ - F5, toggle the audible beep generation
+ - F6, toggle 7/8 bit char width
+ - F7, toggle display functions. this means that control codes
+ in the range 0x00 to 0x1f are not EXECUTED by the emulator
+ any longer, but displayed on the screen
+ - F8, toggle automatic cursor wraparound at end of line
+
+ these functions operate just for the selected screen
+
+
+IV. Keyboard VT220 compatibility
+====================================
+
+ The following keys behave different as probably expected.
+
+ They were mapped to provide more VT220 compatibility.
+
+ To have F1-F8 emit something in HP-mode, you have to switch
+ to user function key labels by using ALT-F10 (see ALT-F10 above) !
+
+ PC Keyboard VT220 Keyboard
+ ------------- --------------------------------------------------
+ F1 F6 (ESC [ 17 ~)
+ F2 F7 (ESC [ 18 ~)
+ F3 F8 (ESC [ 19 ~)
+ F4 F9 (ESC [ 20 ~)
+ F5 F10 (ESC [ 21 ~)
+ F6 F11 (ESC [ 23 ~)
+ F7 F12 (ESC [ 24 ~)
+ F8 F13 (ESC [ 25 ~)
+ ALT-F1 F14 (ESC [ 26 ~)
+ ALT-F2 HELP (ESC [ 28 ~)
+ ALT-F3 DO (ESC [ 29 ~)
+ ALT-F4 F17 (ESC [ 31 ~)
+ ALT-F5 F18 (ESC [ 32 ~)
+ ALT-F6 F19 (ESC [ 33 ~)
+ ALT-F7 F20 (ESC [ 34 ~)
+ ALT-F8 F21 (ESC [ 35 ~) (i know !!!!)
+
+ SHIFT-F1 ... SHIFT-F8
+ User Defined Keys for F6 - F13
+
+ ALT-SHIFT-F1 ... ALT-SHIFT-F8
+ User Defined Keys for F14 - F20
+
+ Insert Insert Here (ESC [ 2 ~)
+
+ Delete Remove (ESC [ 3 ~)
+
+ Home Find (ESC [ 1 ~)
+
+ End Select (ESC [ 4 ~)
+
+ PgUp Prev Screen (ESC [ 5 ~)
+
+ PgDn Next Screen (ESC [ 6 ~)
+
+ PrtSc (ignored)
+
+ Pause (ignored)
+
+ Break (ignored)
+
+ Cursor Keys dependent upon state of cursor key mode either
+ ESC [ A ... ESC [ D or ESC O A ... ESC O D
+
+ NumLock toggles Numeric Keypad for Keypad Numeric Mode
+ and Keypad Application Mode
+
+ ALT-Keypad-0 can be used to generate any keycode in the
+ ALT-Keypad-1 rage 0-255.
+ ALT-Keypad-2 this was modeled with the behaviour of a
+ ALT-Keypad-3 popular boot loader in mind
+ ALT-Keypad-4
+ ALT-Keypad-5
+ ALT-Keypad-6
+ ALT-Keypad-7
+ ALT-Keypad-8
+ ALT-Keypad-9
+
+ Numeric Keypad dependent upon state of keypad numeric/application
+ mode either 0 ... 9 or ESC O p ... ESC O y
+
+ ALT-NumLock emits PF1 sequence (ESC O P)
+
+ ALT-Keypad-/ emits PF2 sequence (ESC O Q)
+
+ ALT-Keypad-* emits PF3 sequence (ESC O R)
+
+ ALT-Keypad-- emits PF4 sequence (ESC O S)
+
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Doc/Keyboard.VT b/usr.sbin/pcvt/Misc/Doc/Keyboard.VT
new file mode 100644
index 0000000..4353ec7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Keyboard.VT
@@ -0,0 +1,231 @@
+================================================================================
+# #
+# Function key mapping for the "more VT220" - like layout #
+# #
+================================================================================
+
+
+I. Special Keys Used To Change The General Runtime Functionality
+=====================================================================
+
+
+ CTRL-F9 Function key labels ON / OFF
+ ------------------------------------
+
+ this key is only operational, when in HP/VT mode, see CTRL-F11
+
+ CTRL-F9 toggles between function key labels displayed or not.
+
+ ON: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+ OFF: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ no function key labels displayed
+ no row/col display
+ no status/load avg line
+
+ applicable escape sequences:
+
+ switch OFF:
+ ESC & j @ remove labels from screen
+
+ switch ON:
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ CTRL-F10 User / System Function key labels
+ -------------------------------------------------
+
+ this key is only operational, when in HP/VT mode, see CTRL-F11
+
+ CTRL-F10 toggles between the display of user or system
+ function key labels
+
+ this key is only active if labels are toggled on via
+ the CTRL-F9 function key
+
+ screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+
+ applicable escape sequences: (see above)
+
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ CTRL-F11 Toggle between pure VT and HP-VT Emulation
+ ----------------------------------------------------------
+
+ This key switches between pure vt100/vt220 mode and
+ vt100/vt220 with hp-fkey-labels mode of operation.
+ this is not dependent of any screen resolution the
+ virtual consoles are in.
+
+ initially, after power on, all virtual screens are in
+ the pure vt-mode:
+
+ - the emulator does not execute any hp escape sequences
+ - the function key label lines are switched off
+ - no load average or status line is displayed
+ - no row / column counter is displayed
+ - the full screen 25x80, 28x80 or 50x80 is usable
+ - CTRL-F9 and CTRL-F10 have no function
+
+ when toggled to the vt/hp mix of operation, the following
+ changes take place:
+
+ - the emulator executes the supported hp-esc sequences
+ - the function key labels are displayable depending
+ on the state of CTRL-F9/CTRL-F10
+ - the load average is displayed
+ - the row / column counters are displayed
+ - in any screen resolution, the last 3 lines are lost,
+ so one has a 22x80, 25x80 and 47x80 resolution
+ - CTRL-F9 and CTRL-F10 are operational.
+
+ when switching modes by means of CTRL-F11, the following
+ changes to the current environment, the emulator may
+ be in, apply:
+
+ - the screen is cleared
+ - the cursor moves to the home position
+ - the scrolling region is reset to default
+
+ (this functionality is available via an ioctl)
+
+ F5 Cycle current page
+ --------------------------
+
+ This key cycles through the display of the video
+ pages. on startup, page 0 is displayed; with every
+ keypress of F5, the next page is displayed, wrapping
+ from the maximum page number back to page 0.
+
+ F1 switches to page 0
+ F2 switches to page 1
+ F3 switches to page 2
+ F4 switches to page 3
+
+ Shift-F1 -- select screen 4
+ Shift-F2 -- select screen 5
+ Shift-F3 -- select screen 6
+ Shift-F4 -- select screen 7
+ Shift-F5 -- select (current screen - 1)
+
+ (this functionality is available via an ioctl)
+
+
+II. Special Keys used to change the Runtime Functionality of a Page
+=======================================================================
+
+ when in mixed HP/VT mode, one has two types of function key labels
+ on screen, user function keys and system function keys. they are
+ accessed by using the CTRL key while pressing a function key.
+
+ the function key labels are NOT available in pure VT220 mode--although
+ the function keys are still active
+
+ the user function keys and their labels can be reprogrammed by use of
+ escape sequences.
+
+ in system function key mode, several pre-programmed functions inside
+ the emulator can be toggled, currently implemented are:
+
+ - CTRL-F1, toggle 80/132 columns
+ - CTRL-F2, soft reset emulator
+ - CTRL-F3, toggle force 24 lines mode
+ - CTRL-F4, toggle keyboard debugging (if compiled in)
+ - CTRL-F5, toggle the audible beep generation
+ - CTRL-F6, toggle 7/8 bit char width
+ - CTRL-F7, toggle display functions. this means that control codes
+ in the range 0x00 to 0x1f are not EXECUTED by the emulator
+ any longer, but displayed on the screen
+ - CTRL-F8, toggle automatic cursor wraparound at end of line
+
+ these functions operate just for the selected page
+
+
+III. Keyboard VT220 compatibility
+====================================
+
+ The following keys behave different as probably expected.
+
+ They were mapped to provide more VT220 compatibility.
+
+
+ PC Keyboard VT220 Keyboard
+ ------------- --------------------------------------------------
+ F6 - F12 emit the sequences for VT220 F6 - F12 keys
+ (ESC [ 17 ~ .... ESC [ 24 ~)
+
+ ALT F1 - F10 emit the sequences for VT220 F11 - F14, HELP, DO,
+ F17 - F20 keys
+ (ESC [ 23 ~ .... ESC [ 34 ~)
+
+ SHIFT F6 - F12 emit the User Definable Key sequences for VT220
+ F6 - F12 keys or
+ (ESC [ 17 ~ .... ESC [ 24 ~) for cleared sequences
+
+ ALTSHIFT F1-F10 emit the User Definable Key sequences for VT220
+ F11 - F14, HELP, DO, F17 - F20 keys or
+ (ESC [ 23 ~ .... ESC [ 34 ~) for cleared sequences
+
+ Insert Insert Here (ESC [ 2 ~)
+
+ Delete Remove (ESC [ 3 ~)
+
+ Home Find (ESC [ 1 ~)
+
+ End Select (ESC [ 4 ~)
+
+ PgUp Prev Screen (ESC [ 5 ~)
+
+ PgDn Next Screen (ESC [ 6 ~)
+
+ PrtSc (ignored)
+
+ Pause (ignored)
+
+ Break (ignored)
+
+ Cursor Keys dependent upon state of cursor key mode either
+ ESC [ A ... ESC [ D or ESC O A ... ESC O D
+
+ NumLock toggles Numeric Keypad for Keypad Numeric Mode
+ and Keypad Application Mode
+
+ ALT-Keypad-0 can be used to generate any keycode in the
+ ALT-Keypad-1 rage 0-255.
+ ALT-Keypad-2 this was modeled with the behaviour of a
+ ALT-Keypad-3 popular boot loader in mind
+ ALT-Keypad-4
+ ALT-Keypad-5
+ ALT-Keypad-6
+ ALT-Keypad-7
+ ALT-Keypad-8
+ ALT-Keypad-9
+
+ Numeric Keypad dependent upon state of keypad numeric/application
+ mode either 0 ... 9 or ESC O p ... ESC O y
+
+ ALT-NumLock emits PF1 sequence (ESC O P)
+
+ ALT-Keypad-/ emits PF2 sequence (ESC O Q)
+
+ ALT-Keypad-* emits PF3 sequence (ESC O R)
+
+ ALT-Keypad-- emits PF4 sequence (ESC O S)
+
+ ALT-F11 emits PF1 sequence (ESC O P)
+
+ ALT-F12 emits PF2 sequence (ESC O Q)
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Doc/Makefile b/usr.sbin/pcvt/Misc/Doc/Makefile
new file mode 100644
index 0000000..a6d9660
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+NOOBJ= noobj
+FILES= Acknowledgements Bibliography CharGen \
+ Charsets EscapeSequences Keyboard.HP Keyboard.VT \
+ NotesAndHints
+FILESDIR= ${BINDIR}/Doc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/Misc/Doc/NotesAndHints b/usr.sbin/pcvt/Misc/Doc/NotesAndHints
new file mode 100644
index 0000000..8accc2f
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/NotesAndHints
@@ -0,0 +1,323 @@
+# $FreeBSD$
+
+Random Notes and Hints Last Edit-Date: [Sun Apr 2 18:28:09 1995]
+--------------------------------------------------------------------------------
+
+
+First of all, please read the file BugList in this directory !
+
+
+Can't get pcvt working on a ThinkPad
+===============================================================================
+
+Anyway, back to the keyboard. The problem is that by default the
+ThinkPad uses PS/2 scan code mode.
+
+You can fix this by using an option and building a kernel, as shown
+below.
+
+] Just for the record, in case someone else is asking for this: Al's
+] confirmation that pcvt w/ PCVT_SCANSET=2 works for the ThinkPad:
+]
+] As Al Elia wrote:
+] | Date: Mon, 28 Nov 1994 18:24:42 GMT
+] | From: Al Elia <aelia%aelia.student.harvard.edu@sax.sax.de>
+] | Message-Id: <199411281824.SAA01554@aelia.student.harvard.edu>
+] | To: joerg_wunsch@bonnie.tcd-dresden.de
+] | Subject: Re: Anyone got FreeBSD 1.1.5.1 running on a ThinkPad?
+] |
+] | PCVT_SCANSET=2 worked...I had put in PCVT_SCAN_SET=2 (Doh!)
+] |
+] | --Al Elia
+] | <aelia@aelia.student.harvard,edu>
+
+ (Terry Lambert quoting Joerg Wunsch quoting Al Elia)
+
+
+If one of the "lock" keys is pressed, LEDs do not get updated, keyboard hangs
+===============================================================================
+
+This entry used to be a long time in the BugList file, and i could never
+reproduce the problem. Today i got an explanation in german from someone
+owning such a keyboard, i'll try to translate:
+
+"This are old keyboards manufactured (~1985/1986) which manage their LED
+ setting only internally.
+ It is not possible to set the LEDs from the (main-) processor, if you
+ try, the keyboard processor hangs and the PC has to be reset by switching
+ power on and off, hard- and/or softreset does not work in this case.
+ Workaround: recompile pcvt with the LED update removed"
+
+In other words, define PCVT_NO_LED_UPDATE if you have such a beast!
+
+
+Cursor not visible anymore in 40 and 50 lines mode
+===============================================================================
+
+You have programmed an underline cursor in i.e. 28 line mode by doing
+"cursor -s 10 -e 12". Then you switch to 40 line mode using "scon -s 40".
+At this point the cursor is no longer visible because the 40 line font
+is only 10 pixels high and the cursor size is programmed with a value
+expressing its size from the top down and NOT from the bottom up!
+If anyone has a good idea how to solve this problem, please tell me!
+The only solution i see so far is having some sort of "generic" cursor
+sizes/descriptions (i.e. underline, rectangle, block) which are
+recalculated in case of a switch to another line size.
+
+
+386BSD port
+===============================================================================
+
+I don't have access to a 386BSD 0.1 machine anymore so the 386BSD pcvt is
+considered unsupported and will disappear in the future.
+
+386BSD support was dropped with release 3.20.
+
+
+Keyboard hangs after first update of keyboard LED's
+===============================================================================
+
+Define PCVT_NO_LED_UPDATE and recompile pcvt. (Or, get yourself a better
+keyboard. Some keyboards just don't work the documented way, this fact is
+"normally" masked by the manufacturers BIOS but unhides when one accesses
+the hardware directly.)
+
+
+Garbled screen when running vi
+===============================================================================
+
+When the terminal speed in the tty structure is set to low speeds (i.e. 1200
+Baud), pcvt shows a strange behaviour in some environments due to the changed
+screen update sequences from vi.
+
+Please check your shell startup files, /etc/ttys and /etc/gettytab and change
+the baudrate (i.e. by using stty(1)) to a higher value, i.e. 19200 Baud.
+
+Since i'm not a vi specialist, i never managed to find out wheter to blame
+vi or pcvt.
+
+
+Stty influences on the driver
+===============================================================================
+
+There used to be an entry in the BugList:
+
+ (printf with 9 x tab) printf "\n\t\t\t\t\t\t\t\t\tGotcha" works ok,
+ while one tab more: printf "\n\t\t\t\t\t\t\t\t\t\tGotcha" doesn't
+ work (it doesn't print Gotcha at column 80, but at column 131).
+
+This was solved some time ago:
+
+ On another note: if I use stty xtabs, the 'printf "\t\t\t\t\t\t\t"
+ bug goes away. With stty xtabs the tab handling is done in the kernel.
+
+(See also below: "Vttest shows strange results")
+
+
+After running some graphics application, the cursor is stuck on the
+bottom line, though everything else appears well
+===============================================================================
+
+Though this might initially appear to be a driver problem, it's rather
+an application program's bogosity. The cursor update is done asynchron-
+ously (to gain output speed), but this cursor update is inhibited while
+an application has put a virtual terminal into ``graphics mode'' (i.e.,
+the application program tells the driver that it's now responsible for
+anything and all on this vty). This is notably the case while X11 is
+running.
+
+If the application fails to properly shut down itself, the terminal
+might be left in an undefined state. The driver stand no chance there,
+even if it could detect this bad status, since it doesn't know enough
+about each piece of hardware to deal with. One possibility is that
+the X server has been shot up and didn't get it to do its cleanups.
+Another case (which i've often noticed on my slow notebook) is, killing
+the Xserver is too slow for the (unfortunately hard-coded) 10-second
+timeout from xinit, so it's being aborted ridiculously. (``X server
+slow to shut down, sending KILL signal.'') This way, the state of
+damage might range from ``almost okay, but cursor is stuck'' up to
+a totally unusable machine (moon bitmap from xphoon still displayed,
+no keyboard responses, only network is working and can be used to
+shut down cleanly).
+If the state of damage is only minimal, you might try to run the pure
+X server on that vty again, and exit it with Ctrl-Alt-BkSpc. This might
+be a workaround.
+
+
+Vttest shows strange results
+===============================================================================
+
+Verify your stty "oxtabs" settings, it has to be "oxtabs", NOT "-oxtabs".
+Get yourself an original DEC terminal to verify vttest's output, i have
+until now not seen any (!) VTxxx clone, which does it right !!!
+
+
+VT220-like Keyboard Layout
+===============================================================================
+
+I have to say, i don't use it and i don't like it, so it's mostly unsupported
+and untested. Patches welcome!
+
+
+132-column mode
+===============================================================================
+
+There are known difficulties running pcvt in 132 column mode in conjunction
+with X. Switching to 132 column mode does not only depend on a given chipset,
+but on the board/manufacturers method of clock generation also. Even if your
+chipset is detected, there may be still a problem with your board and it's
+method of generating clocks. You may run in severe difficulties if your
+board has a programmable clock generator and you run X and you switch from
+132 col mode into X and back.
+
+I have currently no idea how to solve this, other than having a similar
+scheme as XFree86 applied to pcvt: Letting the user probe his board by using
+SuperProbe and recompiling pcvt according to the result.
+
+I stumbled a bit deeper into this with my ELSA Winner 1000, which is equipped
+with an ICD2061 clock synthesizer chip. For 132 column mode to work properly,
+clock generator 2 must deliver 40 MHz to the S3 VGA chip, but this value has
+to be programmed or initialized. If this VGA board has ever been switched
+into 132 colums, i.e. in my case from a DOS program, it will continue to do
+so until X runs or the machine is power cycled. If that occurs, the clock
+generator 2 does contain nothing or garbage (in case of power cycling) or it
+does contain the value for the current resolution in X in case of having been
+in the X Server screen recently.
+
+The X Server reprograms the clock generator each time the server is entered,
+so the only thing to do is to reprogram the clock generator too when pcvt is
+entered. Until now i found no way of identifying the clock oscillator chip
+used, so an automatic clock switching seems to be a problem.
+
+
+NetBSD 0.9 and Xfree86 2.0
+===============================================================================
+
+To get the X server up and running on 0.9, you have to compile pcvt with
+PCVT_USL_VT_COMPAT disabled, otherwise X (and SuperProbe) will hang the
+video driver (not the whole machine !). This bug is reproducible but not
+found yet ...
+This does not apply to NetBSD-current, 386BSD and FreeBSD.
+
+
+X server ioctl compatibility:
+===============================================================================
+
+The compatibility X-Mode ioctl commands CONSOLE_X_MODE_ON and
+CONSOLE_X_MODE_OFF should not be used intermixed with the USL VT style
+commands on another virtual terminal. NB, that this situation could happen
+if you run an XFree86 2.0 server on one virtual terminal and attempt to
+run SuperProbe version 1.0 (as delivered with the XFree86 2.0 release)
+on another vty. SuperProbe is still using the old commands in order to
+gain IO privileges.
+Since the old commands cannot care for things like terminal switching,
+serious corruption could result from this, which need not to be detected
+immediately (i.e., apparently SuperProbe ran well). Known problems are
+font corruptions after the X server has been shut down later, or palette
+flickers in 1-second intervals due to an erroneously re-enabled screen
+saver.
+
+Once that SuperProbe has been fixed in its release to use the USL VT style
+commands, any support for the old CONSOLE_X_MODE_XXX commands will be
+eliminated.
+
+(Recent comment: SuperProbe 1.3 has been fixed. It will be delivered with
+XFree86 2.1.)
+
+
+How to set the foreground intensity to high on VGA mono screens:
+===============================================================================
+
+try to issue the command: "scon -p8,60,60,60", EXPERIMENT !!!
+
+
+How to change the color palette on VGA cards:
+===============================================================================
+
+try out the following commands:
+
+ /usr/local/bin/scon -d/dev/ttyv0 -pblack:0,0,0 -pblue:20,20,40
+ /usr/local/bin/scon -d/dev/ttyv0 -pbrown:55,55,15 -plightgray:0,42,0
+ /usr/local/bin/scon -d/dev/ttyv1 -pblack:42,42,42 -pblue:60,60,60
+ /usr/local/bin/scon -d/dev/ttyv1 -pbrown:60,60,30 -plightgray:30,10,0
+ /usr/local/bin/scon -d/dev/ttyv2 -pblack:42,42,42 -pblue:63,63,63
+ /usr/local/bin/scon -d/dev/ttyv2 -pbrown:60,60,20 -plightgray:0,22,0
+ /usr/local/bin/scon -d/dev/ttyv3 -pblack:38,38,38 -pblue:63,63,63
+ /usr/local/bin/scon -d/dev/ttyv3 -pbrown:60,40,0 -plightgray:0,0,20
+
+ ("scon -p default" resets the colors ...)
+
+
+I have the screensaver compiled in, but can't see any effect
+===============================================================================
+
+Don't forget to turn it on with the scon utility. E.g.,
+
+ scon -t 120
+
+sets the timeout to 2 minutes.
+
+
+Your Notebook uses the NumLock state to switch half of the keyboard into a
+numeric keypad
+===============================================================================
+
+Sigh, each time you leave "vi", your NumLock LED is on again and you
+get a "6" instead of "o"? Try
+
+ options "PCVT_INHIBIT_NUMLOCK"
+
+this prevents applications from turning NumLock on/off (except the
+Xserver - but you want this).
+
+
+Your notebook significantly loses contrast when using pcvt
+===============================================================================
+
+Pcvt turns off the "high intensity" attribute bit internally (to enable
+the use of a 512-characters charset). Some notebooks hard-code the out-
+put intensity versus the character attribute though (i know it for a
+Cirrus Logic CL-GD610/620 chipset).
+
+As a quick & dirty workaround, you can reverse what pcvt did to the
+Attribute Controller. Do not hack pcvt_sup.c, instead patch your
+VGA registers during rc.local with the help of the vgaio utility:
+
+ echo "ar12=0f" | vgaio > /dev/null
+
+For the CL-GD610/620, i'm remapping some attribute registers and
+get a simple gray scale emulation with this (i.e., i DO NOT use
+the hack above):
+
+ eagle_id=`echo 'cr1f?' | vgaio | cut -dx -f2`
+ echo "sr 6 = $eagle_id" | vgaio > /dev/null # enable extended regs
+ echo "sr d5 = 40" | vgaio > /dev/null # not inverse, enable
+ # color emulation
+ echo "ar0=0;ar1=9;ar2=12;ar3=1b;ar4=24;ar5=2d;ar6=36;ar7=3f"|vgaio>/dev/null
+ echo "ar8=0;ar9=9;ara=12;arb=1b;arc=24;ard=2d;are=36;arf=3f"|vgaio>/dev/null
+
+NOTE THAT THIS IS ONLY FROM EXPERIMENTS! There's no warranty that something
+like this wouldn't damage your screen/VGA!
+
+(If you have chipset documentation, you're lucky...)
+
+
+How to set the "LINES"-Environment variable for sh/csh:
+===============================================================================
+
+(Note: this is mostly obsoleted now since the driver properly generates
+SIGWINCH'es to notify applications about a changed screen size.)
+
+ first for the csh:
+
+ alias linesw scon -s \!^ \; setenv LINES \!^
+
+ now for the bash/ash/sh/bash users:
+
+ linesw()
+ {
+ scon -s $1
+ LINES=$1; export LINES
+ }
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Etc/Makefile b/usr.sbin/pcvt/Misc/Etc/Makefile
new file mode 100644
index 0000000..5e1aac5
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+NOOBJ= noobj
+FILES= Termcap Terminfo pcvt.el xmodmap-german pcvt.sh
+FILESDIR= ${BINDIR}/Etc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/Misc/Etc/Termcap b/usr.sbin/pcvt/Misc/Etc/Termcap
new file mode 100644
index 0000000..a4eb030
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Termcap
@@ -0,0 +1,284 @@
+#---------------------------------------------------------------------------
+#
+# pcvt termcap database entry (release 3.20)
+#
+# last edit-date: [Sun Apr 2 18:31:04 1995]
+#
+# -hm new entries for SR and SF
+# -hm removing duplicate entries
+#
+#---------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for pure VT220-Emulation and 25, 28, 35, 40, 43 and
+# 50 lines entries
+# 80 columns
+#---------------------------------------------------------------------------
+pcvt25|dec vt220 with 25 lines:\
+ :li#25:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt28|dec vt220 with 28 lines:\
+ :li#28:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;28r\E[28;1H:\
+ :tc=pcvtXX:
+
+pcvt35|dec vt220 with 35 lines:\
+ :li#35:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;35r\E[35;1H:\
+ :tc=pcvtXX:
+
+pcvt40|dec vt220 with 40 lines:\
+ :li#40:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt43|dec vt220 with 43 lines:\
+ :li#43:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;43r\E[43;1H:\
+ :tc=pcvtXX:
+
+pcvt50|dec vt220 with 50 lines:\
+ :li#50:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;50r\E[50;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for pure VT220-Emulation and 25, 28, 35, 40, 43 and
+# 50 lines entries
+# 132 columns
+#---------------------------------------------------------------------------
+pcvt25w|dec vt220 with 25 lines and 132 cols:\
+ :li#25:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt28w|dec vt220 with 28 lines and 132 cols:\
+ :li#28:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;28r\E[28;1H:\
+ :tc=pcvtXX:
+
+pcvt35w|dec vt220 with 35 lines and 132 cols:\
+ :li#35:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;35r\E[35;1H:\
+ :tc=pcvtXX:
+
+pcvt40w|dec vt220 with 40 lines and 132 cols:\
+ :li#40:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt43w|dec vt220 with 43 lines and 132 cols:\
+ :li#43:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;43r\E[43;1H:\
+ :tc=pcvtXX:
+
+pcvt50w|dec vt220 with 50 lines and 132 cols:\
+ :li#50:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;50r\E[50;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for HP-Emulation and 25, 28, 35, 40, 43 and 50
+# lines entries. note that the HP-Emulation uses the bottom 3 lines
+# for status and function key labels, so we get always 3 lines less.
+# "Xs" is a nonstandard, private flag indicating HP-like fkey labels
+# 80 column entries
+#---------------------------------------------------------------------------
+pcvt22h|dec vt220 with HP-fkey labels and 22 lines:\
+ :li#22:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;22r\E[22;1H:\
+ :tc=pcvtXX:
+
+pcvt25h|dec vt220 with HP-fkey labels and 25 lines:\
+ :li#25:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt32h|dec vt220 with HP-fkey labels and 32 lines:\
+ :li#32:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;32r\E[32;1H:\
+ :tc=pcvtXX:
+
+pcvt37h|dec vt220 with HP-fkey labels and 37 lines:\
+ :li#37:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;37r\E[37;1H:\
+ :tc=pcvtXX:
+
+pcvt40h|dec vt220 with HP-fkey labels and 40 lines:\
+ :li#40:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt47h|dec vt220 with HP-fkey labels and 47 lines:\
+ :li#47:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;47r\E[47;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for HP-Emulation and 25, 28, 35, 40, 43 and 50
+# lines entries. note that the HP-Emulation uses the bottom 3 lines
+# for status and function key labels, so we get always 3 lines less.
+# "Xs" is a nonstandard, private flag indicating HP-like fkey labels
+# 132 column entries
+#---------------------------------------------------------------------------
+pcvt22hw|dec vt220 with HP-fkey labels, 22 lines and 132 cols:\
+ :li#22:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;22r\E[22;1H:\
+ :tc=pcvtXX:
+
+pcvt25hw|dec vt220 with HP-fkey labels, 25 lines and 132 cols:\
+ :li#25:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt32hw|dec vt220 with HP-fkey labels, 32 lines and 132 cols:\
+ :li#32:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;32r\E[32;1H:\
+ :tc=pcvtXX:
+
+pcvt37hw|dec vt220 with HP-fkey labels, 37 lines and 132 cols:\
+ :li#37:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;37r\E[37;1H:\
+ :tc=pcvtXX:
+
+pcvt40hw|dec vt220 with HP-fkey labels, 40 lines and 132 cols:\
+ :li#40:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt47hw|dec vt220 with HP-fkey labels, 47 lines and 132 cols:\
+ :li#47:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;47r\E[47;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# main entry, without "is" and "li" capabilities
+#
+# NOTE: because the 386BSD "vi"/"elvis" seems to have a bug if
+# both "ic" and "im" are specified (an original VT220
+# shows the same buggy behaviour!), "ic" has been taken
+# out of this entry. for reference, it should be "ic=\E[@".
+#
+#---------------------------------------------------------------------------
+pcvtXX|pcvt vt200 emulator (DEC VT220):\
+ :AL=\E[%dL:\
+ :DC=\E[%dP:\
+ :DL=\E[%dM:\
+ :DO=\E[%dB:\
+ :IC=\E[%d@:\
+ :LE=\E[%dD:\
+ :RI=\E[%dC:\
+ :SF=\E[%dS:\
+ :SR=\E[%dT:\
+ :UP=\E[%dA:\
+ :ae=^O:\
+ :al=\E[L:\
+ :am:\
+ :as=^N:\
+ :bl=^G:\
+ :bs:\
+ :cb=\E[1K:\
+ :cd=\E[J:\
+ :ce=\E[K:\
+ :cl=\E[H\E[J:\
+ :cm=\E[%i%d;%dH:\
+ :cr=^M:\
+ :cs=\E[%i%d;%dr:\
+ :ct=\E[3g:\
+ :dc=\E[P:\
+ :dl=\E[M:\
+ :do=^J:\
+ :ei=\E[4l:\
+ :ho=\E[H:\
+ :im=\E[4h:\
+ :it#8:\
+ :k1=\E[17~:\
+ :k2=\E[18~:\
+ :k3=\E[19~:\
+ :k4=\E[20~:\
+ :k5=\E[21~:\
+ :k6=\E[23~:\
+ :k7=\E[24~:\
+ :k8=\E[25~:\
+ :kD=\E[3~:\
+ :kH=\E[4~:\
+ :kI=\E[2~:\
+ :kN=\E[6~:\
+ :kP=\E[5~:\
+ :kb=\177:\
+ :kd=\EOB:\
+ :ke=\E[?1l\E>:\
+ :kh=\E[1~:\
+ :kl=\EOD:\
+ :km:\
+ :kr=\EOC:\
+ :ks=\E[?1h\E=:\
+ :ku=\EOA:\
+ :le=^H:\
+ :mb=\E[5m:\
+ :md=\E[1m:\
+ :me=\E[m:\
+ :mi:\
+ :mr=\E[7m:\
+ :ms:\
+ :nd=\E[C:\
+ :pb#16000000:\
+ :pt:\
+ :rc=\E8:\
+ :rf=/usr/share/tabset/vt100:\
+ :rs=\Ec\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:\
+ :sc=\E7:\
+ :se=\E[27m:\
+ :sf=\ED:\
+ :so=\E[7m:\
+ :sr=\EM:\
+ :st=\EH:\
+ :ue=\E[24m:\
+ :up=\E[A:\
+ :us=\E[4m:\
+ :vt#3:\
+ :xn:
+
+#---------------------------- E O F -------------------------------------------
diff --git a/usr.sbin/pcvt/Misc/Etc/Terminfo b/usr.sbin/pcvt/Misc/Etc/Terminfo
new file mode 100644
index 0000000..072f885
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Terminfo
@@ -0,0 +1,41 @@
+pcvt25h|pcvt vt220 emulator video driver 80 cols,
+ lines#25, cols#80,
+ am, bel=^G, blink=^[[5m, bold=^[[1m, clear=^[[H^[[J,
+ cr=^M, csr=^[[%i%p1%d;%p2%dr, cub1=^H, cub=^[[%p1%dD, cud1=^J,
+ cud=^[[%p1%dB, cuf1=^[[C, cuf=^[[%p1%dC, cup=^[[%i%p1%d;%p2%dH,
+ cuu1=^[[A, cuu=^[[%p1%dA, dch1=^[[P, dch=^[[%p1%dP, dl1=^[[M,
+ dl=^[[%p1%dM, ed=^[[J, el=^[[K, home=^[[H, ht=^I, hts=^[H,
+ ich1=^[[@, ich=^[[%p1%d@, il1=^[[L, il=^[[%p1%dL, ind=^[D,
+ indn=^[%p1%dD, is1=^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h^[[1;25r^[[25;1H,
+ it#8, kbs=^?, kcub1=^[OD, kcud1=^[OB, kcuf1=^[OC, kcuu1=^[OA,
+ kdch1=^[[3~, kf1=^[[17~, kf2=^[[18~, kf3=^[[19~, kf4=^[[20~,
+ kf5=^[[21~, kf6=^[[23~, kf7=^[[24~, kf8=^[[25~, khome=^[[1~,
+ kich1=^[[2~, kll=^[[4~, km, knp=^[[6~, kpp=^[[5~,
+ mir, msgr, nel=^M^J, pb#16000000, rc=^[8, rev=^[[7m,
+ rf=/usr/share/tabset/vt100, ri=^[M, rin=^[%p1%dM, rmacs=^O,
+ rmir=^[[4l, rmkx=^[[?1l^[>, rmso=^[[27m, rmul=^[[24m,
+ rs1=^[c^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h, sc=^[7, sgr0=^[[m,
+ smacs=^N, smir=^[[4h, smkx=^[[?1h^[=, smso=^[[7m, smul=^[[4m,
+ tbc=^[[3g, vt#3, xenl, colors#8, pairs#64, setf=\E[%p1%{30}%+%dm,
+ setb=\E[%p1%{40}%+%dm,
+
+pcvt25hw|pcvt vt220 emulator video driver 132 cols,
+ lines#25, cols#132,
+ am, bel=^G, blink=^[[5m, bold=^[[1m, clear=^[[H^[[J,
+ cr=^M, csr=^[[%i%p1%d;%p2%dr, cub1=^H, cub=^[[%p1%dD, cud1=^J,
+ cud=^[[%p1%dB, cuf1=^[[C, cuf=^[[%p1%dC, cup=^[[%i%p1%d;%p2%dH,
+ cuu1=^[[A, cuu=^[[%p1%dA, dch1=^[[P, dch=^[[%p1%dP, dl1=^[[M,
+ dl=^[[%p1%dM, ed=^[[J, el=^[[K, home=^[[H, ht=^I, hts=^[H,
+ ich1=^[[@, ich=^[[%p1%d@, il1=^[[L, il=^[[%p1%dL, ind=^[D,
+ indn=^[%p1%dD, is1=^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h^[[1;25r^[[25;1H,
+ it#8, kbs=^?, kcub1=^[OD, kcud1=^[OB, kcuf1=^[OC, kcuu1=^[OA,
+ kdch1=^[[3~, kf1=^[[17~, kf2=^[[18~, kf3=^[[19~, kf4=^[[20~,
+ kf5=^[[21~, kf6=^[[23~, kf7=^[[24~, kf8=^[[25~, khome=^[[1~,
+ kich1=^[[2~, kll=^[[4~, km, knp=^[[6~, kpp=^[[5~,
+ mir, msgr, nel=^M^J, pb#16000000, rc=^[8, rev=^[[7m,
+ rf=/usr/share/tabset/vt100, ri=^[M, rin=^[%p1%dM, rmacs=^O,
+ rmir=^[[4l, rmkx=^[[?1l^[>, rmso=^[[27m, rmul=^[[24m,
+ rs1=^[c^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h, sc=^[7, sgr0=^[[m,
+ smacs=^N, smir=^[[4h, smkx=^[[?1h^[=, smso=^[[7m, smul=^[[4m,
+ tbc=^[[3g, vt#3, xenl, colors#8, pairs#64, setf=\E[%p1%{30}%+%dm,
+ setb=\E[%p1%{40}%+%dm,
diff --git a/usr.sbin/pcvt/Misc/Etc/pcvt.el b/usr.sbin/pcvt/Misc/Etc/pcvt.el
new file mode 100644
index 0000000..bd6484f
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/pcvt.el
@@ -0,0 +1,19 @@
+;; pcvt25.el, by J"org Wunsch <joerg_wunsch@uriah.sax.de>
+;;
+;; pcvt is a good VT emulator
+
+(load "term/vt220")
+
+;; ...but i don't like `find' and `select' on `home' and `end' keys
+(global-set-key [find] 'beginning-of-line)
+(global-set-key [select] 'end-of-line)
+
+;; and the f1 thru f8 keys are designated as f6 thru f13
+(define-key function-key-map "\e[17~" [f1])
+(define-key function-key-map "\e[18~" [f2])
+(define-key function-key-map "\e[19~" [f3])
+(define-key function-key-map "\e[20~" [f4])
+(define-key function-key-map "\e[21~" [f5])
+(define-key function-key-map "\e[23~" [f6])
+(define-key function-key-map "\e[24~" [f7])
+(define-key function-key-map "\e[25~" [f8])
diff --git a/usr.sbin/pcvt/Misc/Etc/pcvt.sh b/usr.sbin/pcvt/Misc/Etc/pcvt.sh
new file mode 100644
index 0000000..cc3b34b
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/pcvt.sh
@@ -0,0 +1,163 @@
+#---------------------------------------------------------------------------
+#
+# configure pcvt on system startup example
+# ----------------------------------------
+#
+# This script can be moved to /usr/local/etc/rc.d to
+# configure the pcvt driver at system startup time.
+#
+# Please adjust the values in the configuration
+# section below to suit your needs!
+#
+#---------------------------------------------------------------------------
+#
+# last edit-date: [Fri Mar 31 10:40:18 2000]
+#
+# $FreeBSD$
+#
+#---------------------------------------------------------------------------
+
+############################################################################
+# configuration section
+############################################################################
+
+# path for pcvt's EGA/VGA download fonts
+FONTP=/usr/share/misc/pcvtfonts
+
+pcvt_keymap="de" # keyboard map in /usr/share/misc/keycap.pcvt (or NO).
+pcvt_keydel="0" # key repeat delay, 0-3 (250,500,750,1000 msec) (or NO).
+pcvt_keyrate="5" # keyboard repetition rate 31-0 (2-30 char/sec) (or NO).
+pcvt_keyrepeat="ON" # keyboard repeat ON or OFF (or NO).
+pcvt_force24="NO" # force a 24 line display (when 25 possible) (or NO).
+pcvt_hpext="YES" # use HP extensions (function keys labels) (or NO).
+pcvt_lines="28" # lines (25, 28, 40, 50 or NO).
+pcvt_blanktime="60" # blank time (in seconds) (or NO).
+pcvt_cursorh="0" # cursor top scanline (topmost line is 0) (or NO).
+pcvt_cursorl="16" # cursor low scanline (bottom line is 16) (or NO).
+pcvt_monohigh="YES" # set intensity to high on monochrome monitors (or NO).
+
+############################################################################
+# nothing to configure from here
+############################################################################
+
+# check for correct driver and driver version matching
+
+if ispcvt -d /dev/ttyv0 ; then
+ echo ""
+ echo "configuring pcvt console driver"
+
+# get video adaptor type
+
+ adaptor=`scon -d /dev/ttyv0 -a`
+ echo -n " video adaptor type is $adaptor, "
+
+# get monitor type (mono/color)
+
+ monitor=`scon -d /dev/ttyv0 -m`
+ echo "monitor type is $monitor"
+
+# load fonts into vga
+
+ if [ $adaptor = VGA ] ; then
+ echo -n ' loading fonts: 8x16:0,'
+ loadfont -d /dev/ttyv0 -c0 -f $FONTP/vt220l.816
+ echo -n '1 '
+ loadfont -d /dev/ttyv0 -c1 -f $FONTP/vt220h.816
+ echo -n ' 8x14:0,'
+ loadfont -d /dev/ttyv0 -c2 -f $FONTP/vt220l.814
+ echo -n '1 '
+ loadfont -d /dev/ttyv0 -c3 -f $FONTP/vt220h.814
+ echo -n ' 8x10:0,'
+ loadfont -d /dev/ttyv0 -c4 -f $FONTP/vt220l.810
+ echo -n '1 '
+ loadfont -d /dev/ttyv0 -c5 -f $FONTP/vt220h.810
+ echo -n ' 8x8:0,'
+ loadfont -d /dev/ttyv0 -c6 -f $FONTP/vt220l.808
+ echo '1 '
+ loadfont -d /dev/ttyv0 -c7 -f $FONTP/vt220h.808
+
+# setting screen sizes
+
+ if [ "X${pcvt_lines}" = X"28" ]; then
+ size=-s28
+ echo ' switching to 28 lines'
+ elif [ "X${pcvt_lines}" = X"40" ]; then
+ size=-s40
+ echo ' switching to 40 lines'
+ elif [ "X${pcvt_lines}" = X"50" ]; then
+ size=-s50
+ echo ' switching to 50 lines'
+ else
+ size=-s25
+ echo ' switching to 25 lines'
+ fi
+ fi
+
+# use HP extensions to VT220 emulation ?
+
+ if [ "X${pcvt_hpext}" != X"NO" ] ; then
+ emulation=-H
+ echo " setting emulation to VT220 with HP extensions"
+ else
+ emulation=-V
+ echo " setting emulation to VT220"
+ fi
+
+# for all screens do
+
+ for device in /dev/ttyv*
+ do
+ scon -d$device $size $emulation >/dev/null 2>&1
+ if [ $? != 0 ]
+ then
+ break 1
+ fi
+
+ if [ X${pcvt_cursorh} != X"NO" -a X${pcvt_cursorl} != X"NO" ] ; then
+ cursor -d$device -s$pcvt_cursorh -e$pcvt_cursorl
+ fi
+
+# if monochrome monitor, set color palette to use a higher intensity
+
+ if [ X${pcvt_monohigh} != X"NO" -a $monitor = MONO -a $adaptor = VGA ] ; then
+ scon -d$device -p8,60,60,60
+ fi
+ done
+
+# switch to screen 0
+
+ echo " switching to screen 0"
+ scon -d /dev/ttyv0
+
+# set screensaver timeout
+
+ if [ "X${pcvt_blanktime}" != X"NO" ]; then
+ echo " setting screensaver timeout to $pcvt_blanktime seconds"
+ scon -d /dev/ttyv0 -t$pcvt_blanktime
+ fi
+
+# setup keyboard for national keyboard layout
+
+ if [ "X${pcvt_keymap}" != X"NO" ]; then
+ echo " switching national keyboard layout to $pcvt_keymap"
+ kcon -m $pcvt_keymap
+ fi
+
+# setup keyboard repeat delay value
+
+ if [ "X${pcvt_keydel}" != X"NO" ]; then
+ echo " setting keyboard delay to $pcvt_keydel"
+ kcon -d$pcvt_keydel
+ fi
+
+# setup keyboard repeat rate value
+
+ if [ "X${pcvt_keyrate}" != X"NO" ]; then
+ echo " setting keyboard repeat rate to $pcvt_keyrate"
+ kcon -r$pcvt_keyrate
+ fi
+
+ echo "finished configuring pcvt console driver"
+fi
+
+# EOF
diff --git a/usr.sbin/pcvt/Misc/Etc/xmodmap-german b/usr.sbin/pcvt/Misc/Etc/xmodmap-german
new file mode 100644
index 0000000..61bd100
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/xmodmap-german
@@ -0,0 +1,117 @@
+! German keyboard with a programmer-like mapping,
+! J"org Wunsch <joerg_wunsch@uriah.sax.de>
+!
+! (``programmer-like'' stands for having brackets and braces on diaeresis
+! keys, whereas the ModeShifted keys result in umlauts)
+!
+! Note: the modifier keys are commented out, since they are remapped
+! within Xconfig to match internationalization requirements
+!
+keycode 8 =
+keycode 9 = Escape
+keycode 10 = 1 exclam
+keycode 11 = 2 quotedbl twosuperior
+keycode 12 = 3 paragraph threesuperior
+keycode 13 = 4 dollar
+keycode 14 = 5 percent
+keycode 15 = 6 ampersand
+keycode 16 = 7 slash braceleft
+keycode 17 = 8 parenleft bracketleft
+keycode 18 = 9 parenright bracketright
+keycode 19 = 0 equal braceright
+keycode 20 = ssharp question backslash
+keycode 21 = apostrophe grave
+keycode 22 = Delete
+keycode 23 = Tab
+keycode 24 = q Q at
+keycode 25 = W
+keycode 26 = E
+keycode 27 = R
+keycode 28 = T
+keycode 29 = Z
+keycode 30 = U
+keycode 31 = I
+keycode 32 = O
+keycode 33 = P
+keycode 34 = bracketright braceright udiaeresis Udiaeresis
+keycode 35 = plus asterisk asciitilde
+keycode 36 = Return
+! keycode 37 = Control_L
+keycode 38 = A
+keycode 39 = S
+keycode 40 = D
+keycode 41 = F
+keycode 42 = G
+keycode 43 = H
+keycode 44 = J
+keycode 45 = K
+keycode 46 = L
+keycode 47 = backslash bar odiaeresis Odiaeresis
+keycode 48 = bracketleft braceleft adiaeresis Adiaeresis
+keycode 49 = asciicircum degree
+! keycode 50 = Shift_L
+keycode 51 = numbersign apostrophe
+keycode 52 = Y
+keycode 53 = X
+keycode 54 = C
+keycode 55 = V
+keycode 56 = B
+keycode 57 = N
+keycode 58 = m M mu
+keycode 59 = comma semicolon
+keycode 60 = period colon
+keycode 61 = minus underscore
+! keycode 62 = Shift_R
+keycode 63 = KP_Multiply
+! keycode 64 = Alt_L Meta_L
+keycode 65 = space
+! keycode 66 = Caps_Lock
+keycode 67 = F1
+keycode 68 = F2
+keycode 69 = F3
+keycode 70 = F4
+keycode 71 = F5
+keycode 72 = F6
+keycode 73 = F7
+keycode 74 = F8
+keycode 75 = F9
+keycode 76 = F10
+! keycode 77 = Num_Lock
+keycode 78 = Multi_key
+keycode 79 = KP_7
+keycode 80 = KP_8
+keycode 81 = KP_9
+keycode 82 = KP_Subtract
+keycode 83 = KP_4
+keycode 84 = KP_5
+keycode 85 = KP_6
+keycode 86 = KP_Add
+keycode 87 = KP_1
+keycode 88 = KP_2
+keycode 89 = KP_3
+keycode 90 = KP_0
+keycode 91 = KP_Decimal
+keycode 92 = X386Sys_Req
+keycode 93 =
+keycode 94 = less greater bar
+keycode 95 = F11
+keycode 96 = F12
+keycode 97 = Home
+keycode 98 = Up
+keycode 99 = Prior
+keycode 100 = Left
+keycode 101 = Begin
+keycode 102 = Right
+keycode 103 = End
+keycode 104 = Down
+keycode 105 = Next
+keycode 106 = Insert
+keycode 107 = Delete
+keycode 108 = KP_Enter
+! keycode 109 = Control_R
+keycode 110 = Pause
+keycode 111 = Print
+keycode 112 = KP_Divide
+! keycode 113 = Alt_R Meta_R
+keycode 114 = Break
+
diff --git a/usr.sbin/pcvt/Misc/Makefile b/usr.sbin/pcvt/Misc/Makefile
new file mode 100644
index 0000000..6eff974
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+NOOBJ= noobj
+FILES= README.FIRST
+SUBDIR= Doc Etc
+
+.include "Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/Misc/Makefile.inc b/usr.sbin/pcvt/Misc/Makefile.inc
new file mode 100644
index 0000000..c7a2ebc
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile.inc
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+DISTRIBUTION= doc
+BINDIR= ${SHAREDIR}/pcvt
diff --git a/usr.sbin/pcvt/Misc/README.FIRST b/usr.sbin/pcvt/Misc/README.FIRST
new file mode 100644
index 0000000..a2701c7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/README.FIRST
@@ -0,0 +1,184 @@
+================================================================================
+| |
+| P C V T - VT220 Terminal Emulation Driver |
+| ------------------------------------------- |
+| |
+| (c) Copyright 1992, 2000 by |
+| |
+| Hellmuth Michaelis |
+| Hallstr.20 |
+| Rellingen |
+| Europe |
+| |
+| For the contributors copyrights which apply to parts of the source |
+| see the header sections of the respective source files. |
+| |
+================================================================================
+
+Written by: Hellmuth Michaelis (hm@freebsd.org)
+
+The major contributors to pcvt are Brian and Joerg, pcvt would not be what it
+is without the help, the support and the code from Joerg:
+
+ Brian Dunford-Shore (brian@athe.wustl.edu)
+
+ wrote parts of the EGA/VGA hardware handling and
+ some of the more ugly aspects of the VT220.
+
+ Joerg Wunsch (joerg_wunsch@uriah.heep.sax.de)
+
+ added ALL support for XFree86, the screensaver sub-
+ system and support for FreeBSD (and much more ...).
+
+
+I have to thank the following people for their help, for beta-testing, bugfixes,
+code, keymaps, suggestions, hints, patience and too much more to mention:
+
+ Scott Turner (scotty@gagetalker.com)
+ Peter Galbavy (peter@wonderland.org)
+ Michael Havemester (tik@abqhh.hanse.de)
+ Gordon L. Burditt (gordon@sneaky.lonestar.org)
+ Bruce Evans (bde@runx.oz.au)
+ Heiko W. Rupp (hwr@pilhuhn.ka.sub.org)
+ Carsten Lutz (clu@malihh.hanse.de)
+ Christian Rohrmueller (internal@doitcr.doit.sub.org)
+ Andy Duplain (duplain@rtf.bt.co.uk)
+ Marko Karppinen (dreamer@purkki.apu.fi)
+ Onno van der Linden (c/o frank@fwi.uva.nl)
+ Dave Nelson (dcn@ignatz.acs.depaul.edu)
+ Mark Weaver (Mark_Weaver@brown.edu)
+ John Brezak (brezak@apollo.hp.com)
+ Jan-Oliver Neumann (jan@encap.hanse.de)
+ Kim Andersen (kim@dde.dk)
+ Michael Graff (explorer@iastate.edu)
+ Randy Terbush (randyt@cse.unl.edu)
+ Benjamin Lewis (blewis@vet.vet.purdue.edu)
+ Daniel Harris (daniel@reubio.apana.org.au)
+ Alistair G. Crooks (agc@uts.amdahl.com)
+ Szabolcs Szigeti (pink@bagira.fsz.bme.hu)
+ Charles Hannum (mycroft@gnu.ai.mit.edu)
+ Thomas Gellekum (thomas@ghpc8.ihf.rwth-aachen.de)
+ Matthieu Herrb (matthieu@laas.fr)
+ John A. Perry (perry@jpunix.com)
+ John Kohl (jtk@kolvir.blrc.ma.us)
+ Brian Moore (ziff@eecs.umich.edu)
+ Martin Husemann (martin@euterpe.owl.de)
+ Lon Willett (willett@math.utah.edu)
+ Mark Willey (mwilley@mipos2.intel.com)
+ Bill Sommerfeld (sommerfeld@orchard.medford.ma.us)
+ Rafal Boni (r-boni@uiuc.edu)
+ Thomas Eberhardt (thomas@mathematik.uni-bremen.de)
+
+
+History (see also Doc/ChangeLog)
+--------------------------------------------------------------------------------
+
+Release Changes/Features
+------------- ----------------------------------------------------------------
+1.00 (08/92) Released as "pccons_vga" to alt.sources, VT100 emulation
+
+2.00 (01/93) VT220 emulation, many bugfixes and enhancements
+
+2.10 (03/93) Fixed bugs, monochrome environments, configuration changes
+
+2.20 (10/93) never released (experimental internal release)
+
+3.00 (03/94) Support for XFree86 >= 1.2, support for XFree86 2.0's
+ syscons/USL model for multiple X servers and/or terminal
+ sessions from Joerg Wunsch (Thank You, Joerg !!!), fixed
+ bugs, (n)curses-based EGA/VGA font editor, memory mapped
+ screens, full MDA/CGA virtual screen support, 132 columns
+ on some super VGA chipsets, support for NetBSD >= 0.9,
+ support for FreeBSD >= 1.0 and much more ....
+ (posted to comp.sources.misc, Volume 41, Issue 140-152)
+
+3.10 (08/94) never released (experimental internal release)
+
+3.20 Fast forward/backward scrolling from Michael Havemester,
+ further optimization by Charles Hannum. Keyboard queueing
+ for silo overflow minimization also from Michael.
+ Many bugfixes, cleanups and enhancements.
+ Support for NetBSD 1.0 and FreeBSD 2.0.
+
+
+Features
+--------------------------------------------------------------------------------
+
+The 'pcvt' VT220 emulator driver has:
+
+ - Almost full DEC VT220 (VT100/VT102) Terminal functionality
+ - support for XFree86 >= 1.2 using the pccons model
+ - full multiple virtual screen / multiple X-server support
+ for XFree86 >= 2.0 using the USL-VT/syscons model
+ - Full Support for MDA, CGA, EGA and VGA display adaptors
+ - configurable number of virtual screens on any video board
+ - completely independent virtual terminals for any video board
+ - (24), 25, 28, 40, or 50 lines for each virtual screen on VGA's
+ - (24), 25, 35, or 43 lines for each virtual screen on EGA's
+ - Fully remappable keyboard to support national keyboards
+ - All VT220 character sets plus ISO Latin-1 and DEC Technical supported
+ - VT220 downloadable character set supported when run on EGA/VGA
+ - VT220 user defined keys for each virtual terminal
+ - Optional function key label support a 'la Hewlett-Packard
+ - Display function codes (0x00-0x1f/0x90-0xaf) functionality
+ - Optional screen-saving feature
+ - 132 column operation on several VGA chipsets:
+ o Tseng Labs ET3000 and ET4000
+ o Western Digital WD90C11
+ o Trident TVGA9000, TVGA8900B, TVGA8900C, TVGA8900CL
+ o Video 7 1024i
+ o S3 80C928 (board dependent)
+ o Cirrus Logic GD542x (board dependent)
+
+What it cannot:
+
+ - No double wide/high characters
+ - No softscroll
+ - No inverse background
+ - No VT220 printer output support
+ - No VT52 support at all
+ - No 8-bit controls
+ - Only limited AT-keyboard (84 keys) support
+
+
+The entire pcvt package consists of:
+
+ - the VT220 emulating driver itself
+ - complete documentation for installation and operation
+ - termcap/terminfo, pcvt.el, rc.local, /etc/ttys, xmodmap examples
+ - cursor: utility to set the cursor size and shape
+ - fed: curses-based EGA/VGA character set editor
+ - fontedit: utility to edit the vt220 downloadable character set
+ - ispcvt: utility to display the drivers compile time configuration
+ - kcon: utility to setup national keyboard layouts and remap keys
+ - keycap: keyboard mapping database library similar to termcap
+ - loadfont: utility to load up to 4/8 fonts into an EGA/VGA board
+ - mcon: utility to control/configure a keyboard based mouse emulator
+ - scon: utility to runtime configure the video part of pcvt
+ - userkeys: utility to set the VT220 user programmable function keys
+ - vttest: VT100 compatibility torture test program
+ - some color- characterset- and attribute demos
+ - vga and keyboard register-level debugging utilities
+
+
+WYSIWYG - What You Share Is What You Get
+--------------------------------------------------------------------------------
+
+PLEASE, if you fix bugs, add features, hack this driver to work on your
+hardware or simply don't get it up and running, get in contact with me!
+
+ Help us to avoid reinventing the wheel over and over again!
+ -----------------------------------------------------------
+
+The code is far from being perfect, YOU are very welcome to enhance it !
+Please mail bug reports/fixes, suggestions, enhancements & diffs to
+
+ hm@freebsd.org
+
+I will support this driver as my time permits it, feel free to contact me!
+
+Have fun!
+
+Hellmuth
+
+$FreeBSD$
diff --git a/usr.sbin/pcvt/cursor/Makefile b/usr.sbin/pcvt/cursor/Makefile
new file mode 100644
index 0000000..fb06878
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= cursor
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/cursor/cursor.1 b/usr.sbin/pcvt/cursor/cursor.1
new file mode 100644
index 0000000..6e31d4b
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.1
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this 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.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 16:33:23 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt CURSOR 1
+.Os
+.Sh NAME
+.Nm cursor
+.Nd set cursor shape for the pcvt VT220 video driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar device
+.Op Fl n Ar screenno
+.Op Fl s Ar lineno
+.Op Fl e Ar lineno
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the user to set the cursor shape in a given virtual screen
+of the above mentioned driver.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Specifies a device for which the cursor shape is set.
+.It Fl n
+Sets the virtual screen number to apply the following parameters to.
+Not
+specifying this parameter implies the current virtual screen or the screen
+referenced by the -d parameter.
+.It Fl s
+Specifies the starting (top) scanline the cursor should have.
+.It Fl e
+Specifies the last (bottom) scanline the cursor should have.
+.El
+.Pp
+Be aware of the fact that the parameters need to be adjusted for the current
+size of the characterfont in use, on EGA and VGA boards sizes of 8, 14 and
+16 scanlines are currently supported.
+.Sh EXAMPLES
+The command
+.Dq Li cursor -s3 -e10
+sets the cursor on the current virtual screen to a rectangular shape on a
+14 line VGA screen.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr loadfont 1 ,
+.Xr scon 1 ,
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/cursor/cursor.c b/usr.sbin/pcvt/cursor/cursor.c
new file mode 100644
index 0000000..01bdd10
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1992, 1995 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 Brian Dunford-Shore
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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
+ * Hellmuth Michaelis and Brian Dunford-Shore
+ * 4. The name 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 ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ *
+ */
+
+static char *id =
+ "@(#)cursor.c, 3.20, Last Edit-Date: [Tue Apr 4 12:27:54 1995]\n$FreeBSD$";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm adding option -d <device>
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <machine/pcvt_ioctl.h>
+#include <unistd.h>
+#include <paths.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ struct cursorshape cursorshape;
+ int fd;
+ int c;
+ int screen = -1;
+ int start = -1;
+ int end = -1;
+ int dflag = -1;
+ char *device;
+
+ while( (c = getopt(argc, argv, "d:n:s:e:")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'n':
+ screen = atoi(optarg);
+ break;
+
+ case 's':
+ start = atoi(optarg);
+ break;
+
+ case 'e':
+ end = atoi(optarg);
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(start == -1 || end == -1)
+ usage();
+
+ if(dflag == -1)
+ {
+ fd = DEFAULTFD;
+ }
+ else if((fd = open(device, O_RDWR)) == -1)
+ err(1, "ERROR opening %s", device);
+
+ if(screen == -1)
+ {
+ struct stat stat;
+
+ if((fstat(fd, &stat)) == -1)
+ err(1, "ERROR opening %s", device);
+ screen = minor(stat.st_rdev);
+ }
+
+ cursorshape.start = start;
+ cursorshape.end = end;
+ cursorshape.screen_no = screen;
+
+ if(ioctl(fd, VGACURSOR, &cursorshape) == -1)
+ err(1, "cursor - ioctl VGACURSOR failed, error");
+ else
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\ncursor - set cursor shape for pcvt video driver\n");
+ fprintf(stderr,"usage: cursor -d [device] -n [no] -s [line] -e [line]\n");
+ fprintf(stderr," -d <device> device to use (%svX), default current\n", _PATH_TTY);
+ fprintf(stderr," -n <no> screen no if specified, else current screen\n");
+ fprintf(stderr," -s <line> start scan line (topmost scan line)\n");
+ fprintf(stderr," -e <line> ending scan line (bottom scan line)\n\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/demo/Makefile b/usr.sbin/pcvt/demo/Makefile
new file mode 100644
index 0000000..ee2d1ef
--- /dev/null
+++ b/usr.sbin/pcvt/demo/Makefile
@@ -0,0 +1,56 @@
+# $FreeBSD$
+
+PROG= playvt
+NOMAN= #true
+SRCS= playvt.c
+
+DEMOS= chardemo.vt colors.vt sgr.vt
+DEMOS+= outerlimit.vt twzone.vt cowscene.vt xmas.vt
+CLEANFILES+= ${DEMOS}
+
+all: $(DEMOS) $(PROG)
+
+install:
+ @${ECHO} "to look at the demos, execute:"
+ @${ECHO} " \"cat <filename>.vt\""
+ @${ECHO} "if it is an animation and it runs too fast, try out:"
+ @${ECHO} " \"playvt -f <filename>.vt -d<some-delay-val>\""
+
+.include <bsd.prog.mk>
+
+# this seems to be the lowest common denominator
+
+chardemo.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+colors.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+sgr.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+cowscene.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+xmas.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+outerlimit.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
+
+twzone.vt: ${.CURDIR}/${.TARGET}.gz.uu
+ uudecode ${.CURDIR}/${.TARGET}.gz.uu
+ gunzip -f ${.TARGET}.gz
+ rm -f ${.TARGET}.gz
diff --git a/usr.sbin/pcvt/demo/README b/usr.sbin/pcvt/demo/README
new file mode 100644
index 0000000..5e33f54
--- /dev/null
+++ b/usr.sbin/pcvt/demo/README
@@ -0,0 +1,20 @@
+- The files "chardemo.vt" and "colors.vt" are taken from the MSDOS Kermit
+ distribution and are redistributed with permission from Frank da Cruz.
+
+- cat chardemo.vt - displays all available character sets
+
+- cat colors.vt - displays all available back/foreground color combinations
+
+- cat sgr.vt - displays all possible graphic renditions
+
+- all other files are some VT100 animations collected over the time from
+ unkown sources, play them with playvt.
+
+- Playvt is a program to play an animation file on pcvt: because pcvt is
+ _much_ faster than an original VT100/VT220, it adds a programmable delay
+ for each character so one can enjoy smooth-running animations! :-)
+ There is no manpage available for playvt, just try to run playvt -? which
+ should give sufficient information. You have to empirically find out which
+ delay value fit's your needs!
+
+have fun!
diff --git a/usr.sbin/pcvt/demo/chardemo.vt.gz.uu b/usr.sbin/pcvt/demo/chardemo.vt.gz.uu
new file mode 100644
index 0000000..f26c6fc
--- /dev/null
+++ b/usr.sbin/pcvt/demo/chardemo.vt.gz.uu
@@ -0,0 +1,54 @@
+# $FreeBSD$
+begin 644 chardemo.vt.gz
+M'XL("):V^BX"`V-H87)D96UO+G9T`*7829,=5Q5%X3D1X!]0DZ(1($`F[VLR
+M\UI"IF3`1V",<=DT%IT0!MF6)6%D^N:OLW*OO!',J<E>\09U[N2;Y,6CBP>'
+MVZT^^YE+_MY\^.*#9T\?/KF\O+I^[?[][:<W^.GIK79Y>?WI\\O7/^&7Z^>/
+MC,MWW[J^WO;RG?<?/=[VZLF+6V\_^]A_]K]_]T9<C;AQWF,:O]P9<7=$V__1
+MX7@ZS\O_'Q</YMN'NIQ>H9:MVE;K5H>M^E;'K=JTY2G9MCPG#UO.R>.62_*T
+MY9H\;]F3VZF64VV[U7*K;<=:CK7M6LNUPW:MY=IAN]9R[=73X?'%\KGMT7/E
+MQ7-]?GON7%_8WCK7%_/0N;Z45\YU(T^<Z\MYWUQ?R>/F^FI>-M?-/&NNK^5-
+M<WT]#YKK&WG-7+?RE+E>SCOF^N9V>ZEIN[U4VVXO==AN+W7,[:5.N;W4.;>7
+MFG-[J26WEUIS>ZF>VTN]DMM+W<[MI>[D]E+?RNVE[N;V4J]NM]?Z]G9[K:OM
+M]EKWMMMKO9;;:WTGM]?Z;FZO];W<7NOUW%ZK<GNM^[F]UO=S>ZT?Y/9:;^3V
+M6C_,[;7>S.VU?K3=[O76=KO7C[?;O=[>;O>ZSNU>[^1VKW=SN]=/<KO73W.[
+MU\]RN]?/<[O7>[G=ZT%N]_I%;O?Z96[W^E5N]_KU=KM-]9OM./MPN\[^=CO/
+M/LI]XG=Y`/%^7D#\/D\@_I`W$(_S".*#O(+X,,\@/LH[B"=Y"/%Q7D(\S5.(
+M9WE+J^=Y2ZL_YBVM/LE;6OW)M[1ZX5M:?>I;6OW9M[3ZBV]I]5??TNIOOJ75
+MWWU+JW_XEE;_]"VM_N5;6OW;M[3ZSTL7:TP\N;AY;V?1SKI@`X.-#%8:A#8(
+M<1#J(.1!Z(,0"*$00B*$1@B1$"HAPJ3-.F$#A8T45BJ$5@BQ$&HAY$+HA1`,
+MH1A",H1F"-$0JB'"IBVZ80.'C1Q6.H1V"/$0ZB'D0^B'$!"A($)"A(8($1$J
+M(L*HK3IB`XF-)%9*A)8(,1%J(N1$Z(D0%*$H0E*$I@A1$:HB9-5W5GUGU7=6
+M?;#J@U4?K/I@U0>K/ECUP:H/5GVPZH-5'ZSZ8-5E=9ADQ885&U:LK`A9$;(B
+M9$7(BI`5(2M"5H2L"%D1LB)D1835K:L=U.$D*#:@V(!B!44(BA`4(2A"4(2@
+M"$$1@B($10B*$!0A*"*@#F=!L0'%!A0K*$)0A*`(01&"(@1%"(H0%"$H0E"$
+MH`A!$0%UF`7%!A0;4*R@"$$1@B($10B*$!0A*$)0A*`(01&"(@1%!-1A$10;
+M4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014(=54&Q`L0'%"HH0%"$H
+M0E"$H`A!$8(B!$4(BA`4(2A"4(2@^@ZJ[Z#Z#JH/4'V`Z@-4'Z#Z`-4'J#Y`
+M]0&J#U!]@.H#5!^@NJ!NWCCOHHY'1;$1Q484JRA"482B"$41BB(412B*4!2A
+M*$)1A*((11$1=3PIBHTH-J)811&*(A1%*(I0%*$H0E&$H@A%$8HB%$4HBHBH
+MXUE1;$2Q$<4JBE`4H2A"482B"$41BB(412B*4!2A*$)11$0=9T6Q$<5&%*LH
+M0E&$H@A%$8HB%$4HBE`4H2A"482B"$41$75<%,5&%!M1K*((11&*(A1%*(I0
+M%*$H0E&$H@A%$8HB%$5$U'%5%!M1;$2QBB(412B*4!2A*$)1A*((11&*(A1%
+M*(I0%*&H:0=U.@B*#2@VH%A!$8(B!$4(BA`4(2A"4(2@"$$1@B($10B*"*C3
+M45!L0+$!Q0J*$!0A*$)0A*`(01&"(@1%"(H0%"$H0E!$0)U.@F(#B@TH5E"$
+MH`A!$8(B!$4(BA`4(2A"4(2@"$$1@B("ZG06%!M0;$"Q@B($10B*$!0A*$)0
+MA*`(01&"(@1%"(H0%!%0IUE0;$"Q`<4*BA`4(2A"4(2@"$$1@B($10B*$!0A
+M*$)01$"=%D&Q`<4&%"LH0E"$H`A!$8(B!$4(BA`4(2A"4(2@"$$1@KJS@SHW
+M0;$!Q084*RA"4(2@"$$1@B($10B*$!0A*$)0A*`(01$!=3X(B@TH-J!801&"
+M(@1%"(H0%"$H0E"$H`A!$8(B!$4(B@BH\U%0;$"Q`<4*BA`4(2A"4(2@"$$1
+M@B($10B*$!0A*$)01$"=3X)B`XH-*%90A*`(01&"(@1%"(H0%"$H0E"$H`A!
+M$8(B`NI\%A0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014.=94&Q`
+ML0'%"HH0%"$H0E"$H`A!$8(B!$4(BA`4(2A"4(2@[HZ/==/^M6[:/]=-^_>Z
+M:7RPF\87NVE\LIO&-[MI?+2;QE>[:7RVF\9WNVE\N)O&E[MI?+J;QK>[25!S
+M$Q0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014/-!4&Q`L0'%"HH0
+M%"$H0E"$H`A!$8(B!$4(BA`4(2A"4$1`S4=!L0'%!A0K*$)0A*`(01&"(@1%
+M"(H0%"$H0E"$H`A!$0$UGP3%!A0;4*R@"$$1@B($10B*$!0A*$)0A*`(01&"
+M(@1%!-1\%A0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!0AJ#9`]1U4
+MWT'U'50?H/H`U0>H/D#U`:H/4'V`Z@-4'Z#Z`-4'J#Y`]?UK^+1_#I_V[^'3
+M_D%\&E_$I_%)?!K?Q*?Q47P:7\6G\5E\&M_%I_%A?!I?QJ?Q:7P:W\:G\7%\
+M$M32!,4&%!M0K*`(01&"(@1%"(H0%"$H0E"$H`A!$8(B!$4$U'(0%!M0;$"Q
+M@B($10B*$!0A*$)0A*`(01&"(@1%"(H0%!%0RU%0;$"Q`<4*BA`4(2A"4(2@
+M"$$1@B($10B*$!0A*$)01$`M)T&Q`<4&%"LH0E"$H`A!$8(B!$4(BA`4(2A"
+64(2@"$$1?CT_'&^W^B\8)0@*\AL``,4&
+`
+end
diff --git a/usr.sbin/pcvt/demo/colors.vt.gz.uu b/usr.sbin/pcvt/demo/colors.vt.gz.uu
new file mode 100644
index 0000000..456e6dc
--- /dev/null
+++ b/usr.sbin/pcvt/demo/colors.vt.gz.uu
@@ -0,0 +1,16 @@
+# $FreeBSD$
+begin 644 colors.vt.gz
+M'XL("):V^BX"`V-O;&]R<RYV=`#MU+UNG$`4!>!^I7T"FGD`Q^+^S)TB2N-8
+M;B(GA=UMM;*1C;(8":\E/W[,G(.SX0E2+,WE(#%\@N$T._.O7H;MYO;NR_6O
+MN_2CFX;^F.RR3??=^S'=CH]=^CX>QNGU(ME[^I9NQJE[FL:WE\>+Y/.%J_W#
+M;US8;K:;A*/963LO_'&+MVF^TP5#,0S#,3)&8)24MIMEA63MLEP[I#EYBRB,
+M@JB,BFB,ANB,CI@9,V(PQN=3:RRG#*D,(4/`$#($#"%#P!`R!`PA0\`0,@0,
+M(4/`$#*DOH_T<YR&_>'4H]6C]"@\2H_"H_0H/$J/PJ/T*#Q*C\*C]"@\2H_"
+M\W<;G)JLFHPF@\EH,IB,)H/):#*8C":#R6@RF(PF@\EHLM6G\LIP,AP,)\/!
+M<#(<#"?#P7`R'`PGP\%P,AP,)\-7C%P9F8P,1B8C@Y')R&!D,C(8F8P,1B8C
+M@Y')R&!D,O**$9419`0804:`$60$&$%&@!%D!!A!1H`19`0804:L&*4R"AD%
+MC$)&`:.04<`H9!0P"AD%C$)&`:.04<`H9)1_&/?/_6M:_BZ<UHV]3\MV&MX.
+MQW[YJ`]S\Z7EW1[ZEVY9Z++YK,YF)\.YKU9]=37U3\_'<U^=^^K<5_]57S6[
+-]J.M_@#AP6KE^0D``&Y9
+`
+end
diff --git a/usr.sbin/pcvt/demo/cowscene.vt.gz.uu b/usr.sbin/pcvt/demo/cowscene.vt.gz.uu
new file mode 100644
index 0000000..7a5a4dbb
--- /dev/null
+++ b/usr.sbin/pcvt/demo/cowscene.vt.gz.uu
@@ -0,0 +1,90 @@
+begin 644 cowscene.vt.gz
+M'XL(".BA?2H"`V-O=W-C96YE+G9T`.U=P8[CQA$]&MA?F`N!R6%6B"QU=9.4
+ML`?;"1((1GP*XLL(&!C916QDXPT<(-Z#H&\/V5U-=C<?BV4@<)RD%XA":JA^
+M[W635:\DLOSP3%\^/']&[?N'Y_X-N1]>/3R;XQMS>3UNF+A!PT8S;MAQPV^Y
+M::N-?^R&C=WVOX>GXU]_?/_P])OM0R.;EXG.AXG/`1&*?*8/)I^</WJ=/WN;
+M/WR;Y=R;.,!3,L)3,L0^'2,9Y'!-1IF':=)QFFR@=*0F'6I_2Q@UZ6#Y:,5P
+MV7CY@/N479,/68Q9#IJ/6@R;,RT'+D=>#%V,G0U^W>>L[\OA%^,O`?;YBN<0
+MMUNAX(Y`EB@9S&&!TRR`FD.IIL%0`"L#:Y9H!P#7+)0U:X`(,8<$F`T`W35`
+M90[;)Q#*?R5;2+?@BP@CQLT.3M3,F=$)H1-")X1.")T\>C%AA-`M0K<(W2)T
+MB]`MTFX1ND/H#J$[A.X0ND/:'4)O$7J+T%N$WB+T%FEO$7J'T#N$WB'T#J%W
+M2'N'T'N$WB/T'J'W"+U'VGN$?D+H)X1^0N@GA'Y"VD\(_8S0SPC]C-#/"/V,
+MM)\1NCG"<'.$\>8(`\X11IPCF@`>M^2`0QZ.>3CHX:B'PQZ,>P8&/@,CGX&A
+MS\#89V#P,S#Z&1C^#(Q_!@9``R.@@2'0P!AH8!`T,`H:&`8-C(,&!D(#(Z&!
+MH=#`6&A@,#0P&AH8#@V,AP8&1`,CHH$AT<"8:&!0-#`J&A@6#8R+!@9&`R.C
+M@:'1P-AH8'`T,#H:&!X-C(\&!D@#(Z2!(=+`&&E@D#0P2AH8)@V,DP3C),$X
+M23!.$HR3=$0<:!DG3T/=>OFB^>V''Q^>SV^HN]P"K^[R]?B9P<V-K^1?K7]U
+M_K7UKYU_[?WKR;^>_>L0D/W_A4\/82F,%08P8003AC!A#!,&,6$4$X:A,`PQ
+MB<""[*4RJ\Q^)F;^^O`7(E\?3;P^FCJCE5F]/FK^J,PJLYH_*K/*K.:/RJPR
+MJ_FC,JO,*K/*K#+[.9@-=-SE$'[H;L>-\QMK+U]]^/#IIW5**[/*S%\@3;Q`
+M&KY`IGMCZI169O4"J1FD,JO,:@:IS"JSFD$JL\JL9I#*K#*KS"JSRNS_FYE[
+M8_O+U^%9J-[_0D]C]OJB>?_-]V__]MWW[ZK)J\PJLW"=-/$Z:>)U4CR+66>V
+M,JO72<TGE5EE5O-)95:9U7Q2F55FOY1\<AP_C)Y*AIT3"'9.(-@Y@0QLV8([
+MQN"6,;AG#&X:@[O&$'PR&G9.(-@Y@6#G!(*=$PAV3B`+YP%V3B#8.8%@YP2"
+MG1,(=DX@!^<!=DX@V#F!8.<$@IT3"'9.H!;.`^R<0+!S`L'."00[)Q#LG$`=
+MG`?8.8%@YP2"G1,(=DX@V#F!>C@/R\X)OKW&PZ/]W<>_O__P]KOO_S(^.#.\
+MWX7W7?G^R!EV6B#8:8%@IP6"G1;H!.<M[[3PZI/0%JF[7&^'\6F&(1K]X>'Y
+MJX?GS]IO.2*UE^LP]H%;+;G+-<WC]HTSEP,W5QJ.W.UVAU<Q;EW#1LSXCL*1
+MSH^R&]B%8X<@3/Y8$X\=`K*]E'S>O_(_RIWF=G-'?WSS]/GGL674J([GA<:]
+M89:8]\!F%[:ZR^X6-MWQ,K$]7@XA\-(Y;/D9W(4>#,6LG%(63.GCQT@IYT`!
+MEV9<&J=L4F["W\W\=S-.5/R[&SM"Q+6_-4U<\%M"D6?_Z%N[#8R'_]WO/-'#
+MXAR:G3]?=E<PH7TJY91*.6=*CBF3?F;23TQZ9F)#QHISW?#F\/$KFLN!P#DE
+M<$X)G-.I')\]B[#=!-L%V%+7R?.=ELCO3$ODI:1+=-C')1KF*2[1_N#IN="3
+M(\;.B4([46B7%+X-J"8]5_T><S!^CSF<PD[$<#.&FS#<O,Y39ID6V7(?P/T5
+M+?$(36F/1K_'1/Q5&8F<PTXD,IR:_&!32-97WO'Y,=#Q)[!_T\R+'1!B:S=_
+M6K^*%_5$@\(>T[!^+SG9;,9CHM#$YBG#Z3P##.%I6LJ\M5XX"X=K^Y!,4,1F
+M)C9CXC(F)C*9@'G;]XM)2/27^3)\?`Q@CX_\L8Q`$Z^2XR4CRJN6,&)^+N/7
+M9OQHXA=:E(ZS[1N+YHGHW=L&)J+Y_6K7*[-:?-4O\RJSRNS?<9UP_FG+=-S!
+M][WW2/M:S]UOK].5QXUOKR\O+[]NLI;*8]T3&]Z^A'^WJ=?M4_,ZCO^K"'";
+M$9X!Q`K&`B1!26'N,TX"U$"D-:@E5@J6HC4SW$N&MP*XB@@@,\P4].7>)).9
+MPMX`[@T"WP#R;04ZPWY))#<Y>H/@&XS?(`(OJPQR"HG^_Q2%;!H*$F\QBS4:
+MF(=`I&"234?)Y<,:F54V*W0D/B6A?'(6E%8XO9-(K;$2:2UX%5-5,OO]&K4_
+MR]Q6R<GLEO3*B2L)-JL,O]NBN,YQ@R1@N9C&DN<_UHG^<YNI0!5R?2V374[J
+M@J[`]P<-88DQGMZ"<TD9S'!)^AN)]3L=;9$W)/ZTR1Q-=\G]*Y'\'[7L9?KX
+M7%D(X.\1"A%P`4H9>UE'HQ>RH63E'%IJV0$E>$%*+7_:$O-3U&S)63NU%H*@
+MGI6U603+;4D_2=.F*+Q*I:P]"E&+A;H)PFZ;RFX*:;=-;;=M<:6Z?:JN>5E?
+M-DE?LRVPT2ALMB4V&HV%R"%@3")I(7):0Y)$TK9(THBD;9&T+9(6(L>@.(FT
+MZRMI)9%V6Z35B+3;(NVV2%N*'#7.(MWZ2CI)I-L6Z30BW;9(MRW2%2*]QEED
+MN[Z2K22RW1;9:D2VVR+;;9%M+C)HG+Q(M[Z0G:2QV];8:31VVQJ[;8U=II$E
+M3AK[=8V]I+'?UMAK-/;;&OMMC7VJ,4H<WTA_P[F//[>LB)Y.WO]2T9-F+WJ/
+M1)_65_HDB3YMBSYI1)^V19^V19]FT;/F8+Z!YO/Z0I\ES>=MS6>-YO.VYO.V
+MYG.B>=<L-.>2Q_]&SZH9/(IVZ:CP2T>583HJ'--189F.L_)9^&-47@@W@G#9
+M)VJ,HLXI:JRBQBL"L]@\1N&%;A+LL6@=C<([&I5Y-`KW:!3VT2S]XR!['W07
+MLJVPW**9-`HW:51VTBC\I%$82K-PE*/J(+M0[83%%MVE4=A+H_*71F$PC<)A
+MFM)B>M%>=2&Z%99:=)M&83>-RF\:A>$T"L?I_W-5^T1T<"C>JF2:.V&A1?=I
+M%/;3J/RG41A0HW"@XS'[1#.;LE%S)EGPHD;T949AS(S*F1F%-3,*;S8<$XK[
+M(#G:4+^7*#X)BRR:,J-P949ERXS"EQF%,1N."96^UQ@%>\6)X+.PQ*(C,PI+
+M9E2>S"A,F5&XLN&8W12^)KUCCK[/-^+1<7V%271CI'!CI')CI'!CI'!CE+BQ
+M6>^C7^^HUW:7>_-+ONW!4VSNOWB*=1;K+-99_)^8Q4]\(A"J4Q*K4U)4IZ2J
+M3DE1G9*B.B54G8;OAJ>\)Q2E)/^>H?E!0_>+AN8G#<UO&K0F=U(KU*(DUJ*D
+MJ$5)58N2HA8E12U*%JF]#WO!PI%0@9)8@9*B`B55!4J*"I04%2@YH/4^?3=.
+M0MU)8MU)BKJ35'4G*>I.4M2=U"ZEWN>?`4@H-TDL-TE1;I*JW"1%N4F*<I.Z
+MA=31D4]2A3*3Q#*3%&4FJ<I,4I29I"@SJ2^E^MICDBK4ER36EZ2H+TE57Y*B
+MOB1%?4FG0FJHLB:I0F5)8F5)BLJ25)4E*2I+4E26=,ZE!J635"O4E%:L*:VB
+MIK2JFM(J:DJKJ"GM,9/*2F>I@FNRHFNR"M=D5:[)*ER35;@FF[FFJ'26*C@F
+M*SHFJW!,5N68K,(Q685CLJECFI3.4@6[9.7[0#0W@NCN!-'<"J*Y%R2Q2[/2
+M6:K@EJSHEJS"+5F56[(*MV05;LEF;BDJG:4*;LF*;LDJW))5N26K<$M6X99L
+MZI8FI;-4P2U9T2U9A5NR*K=D%6[)*MR23=S2K'26*K@E*[HEJW!+5N66K,(M
+M685;LHE;.BQO/;:"6[*B6[(*MV15;LDJW))5N"5[6JM9IR?R5U=5=$M6X9:L
+MRBU9A5NR"K=DSZ)4)[@E)[HEIW!+3N66G,(M.85;\CT,[NM2!;?D1+?D%&[)
+MJ=R24[@EIW!+XS%W854%M^1$M^04;LFIW))3N"6G<$O#,7?I!!;<DA/=DE.X
+M):=R2T[AEIS"+86>!^M2!;?DY'MG-3?/ZNZ>=9>G3:GC,5M2!S1?B:](%=R2
+M$]V24[@EIW)+PU$OFU+;R\=-J>TE5.(K4@6WY$2WY!1NR:G<TG#4MM1.(;6[
+M'*1K57!+3G1+3N&6G,HM#4>]WI0Z'K,EM9?SJN"6G.B6G,(M.<DM-9-4R2VQ
+M4LDLL5#9*SG!*SG1*SF%5W*"5V*1@D]BC8)+8HFR1VH%C]2*'JE5>*1VW2--
+M1UF$4ZYENVZ1^';^HRQ3\$>MZ(]:A3]J5_T1"S1;Z]BN^B*69V1Y@B=J14_4
+M*CQ1N^:)$@D6X2Q6<<T2L<BU7]N"1L$,M:(9:E?,4)-J7#-#+,YNKN"*!V)I
+M%DL+R@3OTV+OP\*<0IA3+9[;7CS\/5%L2]@ZJ#`(%!Q/"QT/ZX-^AW5!G\-J
+MX'=!+`-^!\0:6J@AJA#,3`O,S$M4@:P,B^A4B]-M+P[ZNH=%=<+"(-/"BH!G
+M83W]NIY>OHKZS:L(?)?#,GI!!C(D+&/I$UC%THJPAJ7_8/9X*!?^D)-FSB>!
+M\]);,.-%TF?""TL1)WWA(^)T+T:*L[QP#?$B7IB%\#3<PB3PHW!EXN:'X$IG
+MP`^^E6:`'W5#@[CP=@G9^K?7IK0KDCR?`EV1?9EDGM?CPWE%)F>*8`07WEU,
+M85=DZL"LR,],C/#T92F9>64IF%D1GK@LR3(I$F8MRZO,+$UWS"I-HDPJS9G,
+MJ?R@"V\5=/)L&&BD29!9.#P_2=9C(DF.8QX.STV2PYB*D+:Z)&TQHSFQ,)<Y
+M0S&5.2TQD_PC+KR149BS3H"=\PRCQOC/F%,^8<@IB3!B>K@+NRG<E`\"VI0"
+M&*S/P6*P9ZP8X!FJSZ%B]&:D&+(#4`S2C'/*<3@<,PS_D5'"'F-PL&4(CK`!
+M@4,J`YQS@!`^>?SS-*+-#W1A=QH_!,3PZ&B(@ORPZ'$:P?`.CQUV^&G/:=N%
+M;1ZV]U$LC.J#%H>J<8?SD]^,3Y;&M^V\Z>;-UF_.P\5NI\=I;QJQF7JRIGLV
+>.?"2C#BW34UWQQ.;K+_C\C-JOWWU+THZ&OFDJ0``
+`
+end
diff --git a/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu b/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu
new file mode 100644
index 0000000..a2b6d1a
--- /dev/null
+++ b/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu
@@ -0,0 +1,193 @@
+begin 644 outerlimit.vt.gz
+M'XL(",2<V"H"`V]U=&5R;&EM:70N=G0`[9U=C]S(=88O#0R"Y`?H9@#?V#<!
+MZYM<76FU8_>LM1IG-+*\WJL-D@M[K37LK&(;0?Y[2-;[G"*[1^.6+4ZV%RT(
+MF&)WL5AUZCWO^:ABUS]=//G)IT]^VET\^:I[^^0K__F3K\KXU_FGL=M=UH*;
+M"V%1F+_JCJPW%MSN8I-V_4;MAHW:C1NUFS9J-V_4;MFHW7ZC=H>-VG7=5@UO
+MI7%N*Y5S6^F<VTKIW%9:Y[92.[>5WKFM%,]MI7E^UCP_:>#=[NKVZFSVMC9[
+M<=+$ZU=G06\MZ#SI^<N;N]WURY^?I7WVYM;>7/_4Y]V;VYLS-L[8V,>&BV/[
+M;Z[O=F=H;`T-EY\&O_ORYO7M6=:;R[J??/6[JQ=7O[I^=7WS\BSQ,_&MB<]W
+MTP>OKN[.T-@:&G/(-=[V"T4$M3C[K+4X.RBU.-LC%6>^5+F?;OR?IM#_RPQ.
+MWU^<Y_`Q")69\T_]."\_#I_=//DJU'*<RN<YV#J<#K/<QT#OR5>I7L3YXBSY
+M;27?SPSVX_!LE/50R_'9"<K=O9W',J/H[NJ+7];AS#C2Y=3B&4T;<^ELX\89
+MN+F9KM)\%>O5R2&*L;Q9C>7-28_E1DY%FYH3]3*<$==8-N*:7:=*`I^-92<&
+M^*R9^1^'S^L-4SE^/OMB+D^?OQ[+I9;CZRHSUT]?O)K%-9?CJ_D&/S_Y;KYA
+M+L>[LZ/V",[V+.J+4?2SR["\RJNK?G7ENO7E^DZWOM6M[_7K>[V7'NVNQHL@
+M'=J=Z(++.!H_#^>7U\_O7M_6(<V?1/OD;#7/"8:#!,-Y7\T9$_O9]NYIZ'9O
+MSDO/9V3L(\-//M*S4]R5,+L*/]??EQ3FE>C:S/3HN5TJO[BV:B\6Y1LNYF<>
+MW'5[LZQ\M[Y\N;@.]]Y^\W+OEN?MDPN&M7?7U(S[Q?.]6_T]GU9YU;O"O7?%
+M^^]RW)7NO2O?\VE8W%7NO:N__R[KX7#O77.6=O7QV5\_$]4^41$AWMT^>_GJ
+MB^M7=7G-@L7EQV?X;)]+&J=EM!LG:#7\T^QV_M0Z_J-SA[>'QHAIO_OB-#L>
+M=L]/L^-Q=X)+>.<@^^RAW!-*.7\.LA_#^9A3HV^N7[PXR_JLA0=:&'IB[.5C
+M7G+[W<./>;C>(3P^6KM^HW;#1NVBBLGO]A993D/44R3@=[N;V^O?C&AY]H,%
+MBY1BF/RLJ9+?W=;O/``9K%(_>9%3I3#7#E-!E7JK5%#%N'M1*T4J%:N4J93H
+M"H7I*U5*5,I4HC!]I4J12H5*%*:O5"E0J:<2A>DK5?)4&JA$8?I*E9PJ):,K
+M4R1GE6"CA*"M$$SB?J"2U[0D).Y-XKZ?93A6"KM=K83$O4G<%RK%2=&F2DC<
+MF\1]WEW72@C:"MXD/IG-6@E!6\&;Q#T23PC:"MXD[I%X0M!6\"9QC\03@K:"
+M-XE[))Z-R`WE)G&/Q#."MH(WB3LD/NDU\9R]W4>EGDI@/(?V;B&5P/@8GMP2
+MI]B;C50"XSG-H@]3P=ZKI!(8SWGWFUHIM[<ZJ83$<Z%/I;U32J4P3_Y8J9_U
+M/4P%>Z/ULGF@M=(@T.6AO4]+)21>YA3.]);TXFU>*B'QXJ3FQ;5WB54)@1?D
+M;`63-^(N2-D*)FV$79!Q*U`%41<D;`63-((NR-<*)F?$7)"N%1J34`796J&A
+MFBI(U@I-PJK2V^OBS2!<7AC3_ZUJQSWLB"X?,?`CQ'?$)!PQE4<`X@A8+>%Y
+M^3YX+H%^^5Z@(V+3E/M4!B&;SMVG?`LUOGRO&B\(X?*]A+"@ELOW4LN"I"[?
+M2U(+NKM\+]TMB//R?<1Y%`4?1^9'F86C#,Q1I@J)/VCTD/B#YA.)F_V]SQ`C
+M<;/D]YGTA7-P^3[GX"@WXSB'Y2C7YR@GZBAW["C'[B@7<>%L7K[7V43BYJW>
+MY[8B<?-[[W&`_T:8>5Y8.N<;]O(-<;H^)_T>XUV(44W/.;^S#IYS?O_?.;\\
+MJ>))YOQ\-[D-O[JZ/<FNCQ_<79]FS_WN^0\[P^JXT_%=7!14J:-2QW=I4:B5
+M!M49^"9/A:M:Z';5%(Z%Z_EOS<T4ZA9S4]6*?=,O"K5*5I7,-\.B4*LD5:&7
+M?N[WM8'1_)_Y[]Q+S_B]HQ6$BM2\7Q1J%<C>O@F+0F<I$VWK=+]85)W?#`Y[
+M%ZK%PW7+JI;?NVBWQ,4M<5G+[5VT6]+BEK2LU>U=M%ORXI:\J&73P$6[I2QN
+M*<M:_=Y%NZ5?W+*J5?8NVBW#XI957_+>1;O%=8M[W'+,+NU=+&YRRYN6LG5Q
+M[V)QTQ(";CF'+NQ=+.1V]A?/_N(J9NM/,F3+4X;P^;,3_%69?DKL/=\]>_GS
+M<Z1\5L=[WDYRNY-\4;-&GC^[>?YZ]6N3S[GY]<,/>;C>8><_6KM^HW;#1NW&
+MC=I-&[7+;]S%?G=W<WK(&+V[E->[^T^DX_VTO/7JYF=W9W7<6AW'<*:XW:<O
+M7M]>G(5]YK[W_9+_1VNW;-1NOU&[PT;M[O^2_\=K>"N-FW[)_Y_GQ(S;??V1
+M_XT/J$SA_0:M;\ALT^_?G%1_N]/J[_1FZ$GUMS^Q_I83ZV\^L?ZF$^MO/+'^
+MAL?O[]?TX^N'^[NL9_W=W+Y]33^^?KB_#]>S_KH3ZV]W6OT=3JN[_6EUMYQ6
+M=_-I=3>=5G?C:74WG%9W3\VNG1B1G4Z[?J-VPT;MQHW:31NUFTF3C'[P^*^F
+M(,;RS6W=VJ//SZ#9'#13;J*?A7U99V(*_L</7NV>W?[RZF7=R;6L<9Z3[><D
+MSR_5U.E(<_GZKF[?"V?%.+/IO6PZ!HT)T`QS\>YFWFF73A<R\WMVC9KR?/W\
+M]LM7=\]>S-L;TYF8'I68WDX12%Y-2GW]]/F+9[?7=U_.FV'S>5+.1/5^HMJ@
+MW;)1N_U&[0Y;A4/=5@UO%L#9H3SS#P'\\8]__-U$(7VW^\L4WXY_OYD#73]]
+M]1'_U>#Y]S7D_XNMW(;555Q=I=557EV5U56_NAI65W-N=W'IUI?K[KAU?]RZ
+M0V[=([?NDEOWR:T[Y=:]\NM>^76O_+I7\^+YVWH(Q$>>EM_5A\R/G#!0^T*I
+MHS3VGE)OI6*E;*5DI6BE8"5[AK-G.'N&/<*>8`^P]JUY:WV2U'<?5R:7JW_O
+MA-+-'Q(?XR'I,1Z2'^,AY3$>TC_&0X;'>,A,@UL^Y1WDNOE#_&,\)#S&0^)C
+M/"0]QD/R8SRD/,9#^L=XR/`(#_&/H?'^,33>/X;&5R?KOS_JO]^M/;KOOOVH
+M_]ZMW<?M6N^V;+WJPF:M]YNV7C9M/6_:>MJT];AIZV'3UC?55;>IKKI-=753
+M5=U44S=5U$WU=%,UW51+-U72;75T2E7]^:/^^V8A\Y!VWS8GX=OY8/6T>W?9
+MN4_&_]Y??C=A:JK5[O^V_M)$V/E.!3=0Z"D4"IE"HA`I!!IRGH*C0-,=37<T
+MW=%T1]-=HJ&.MKM`@:8[FNXZ_5!&UWV2!NY,-)]*!6,GP<PR&:_>7?[LYK:*
+M8_IN+8E$5Q*C3/0DT9-$3Q(]20PR6C<BW8B,,M)TI.E(TY&F(TU'5SN?=ZWK
+M>7JG61W/NU6W(QT(2#GP_,#S`\\//#_P_&`3&.A`8&R!ICU->YKV-.UIVJ>Y
+MV[[;-9&/%R^O?GU7.SY>[$&//GADX.F"=\?`\^(8?!X#SXMC\'D,/*L(TE($
+M:;>[>2W0C1=K$50<AU)Q?#^4CX'GQ3'X/`:>%\?@\QCD71P#O6.0=W$,](Y!
+MU(74_MT<H_U^-N+3U:OK&:#]/93PO2+'6?4),6OW\^[?7E]?W;WX4@.HU+!&
+MV(>P:>K?RZ;?/XKL5Y,YU*MG+S^;9.&Z>V;S>\64M8?OU'OGZM6;F>3K;U)_
+M^_TERZGS:7?Y5BPW]7Z\G'Y1D^Z+Z!8X_`C4N@?6\@_QY3@&[W8+KVD<Q?B!
+M?OI1XQ@_.-"H,\<^R+$5O*:7=55[5$QA(W[?:?;O8,W\7M;4[Z\O9#%Y<L_N
+M$$7>?8]]4/T&N_5]O/CRYK6Z+LWXGI)K7&-0SO^KJTJO]WG_/TQG=`^HZ1]C
+MS(7[]%W-5BY-[GT.U)DK'^;*A0_S]MN:O)ZN=U?/QHCA1VRE__:'%;.G>$)>
+MYO>/W#X:7<V)Z?HCE^7MR.S:^/7V`#AA'R4-'.6`D<(>_XSS'9;S70XF-^Q/
+M9:.&LB2"^?"9^7C[9[=7>YUML^<.IJH<3$S8GX59K>-T:D25QY[\TX&P35^&
+MNFY3CZ,_4,*'1%CWQ97E$Q\4)]+TDS3M`)PJCT]O7M_M2:1I43C0$!-KO."T
+MF$7WFPC[`\GYI<!\G`XM64HL'PBJ.Y!)U(]GYY7(W`'=I#63C"-WBY$[06'Z
+M3;2_;]QI;[J/'_9\G,JB\T</NW^:ET\\:M#=<M"E#OKJU[^\NKV^>OE\7PV:
+M4KF#,1OHA[ILE%<3T,;J#N`?EF"?CVI:#B(=C*:LP=N-"FV#2-/!,XOG'M_C
+M^3B<.N>[_7$?W?O1PGKWP=WOZ7Z9SB#ZNWK?34=!+9Y[?(_G(W>JFK_9'[?9
+M],;(D4ZOPN-8$TC]2L^'?1O8.#.N`.^F7Q]<ZGD\L"SW=*0ZXCFT9XZ]6L5*
+M\8&>V,D^=>@O/]N?\GA@.._IU;RTXU<D=8^HTOV=JAY:*BNA/20KZTC=`%+\
+M"FG#$9,6%]H^_^ST./8OOGQU=W7[Y;ZJAP/$N7ULS;,W3)NTEVP5#KC)K>EG
+M[$>P?N3IQ*;%,(Y^;B6[#WOLTK9%:?N;W?7SW=\[>/BF^Z!N-$,CNG`?/'KY
+M!!_VW"77)QFXVZMGSW=7K_8%X/<=V6:UW!*&HNM[?)-NG^;"R-+Y8H5^J6^S
+MD?Y@L/V!2Q07L'&'K@UA0!A9U1QC<\Q1_"S.^]GMS1?[FN\.9)K63QD;+R9*
+M:7`#L#M@Z+1/P6[)V?<X*2:RD<Y:C%J68\!+:(Q_3+>3==M-)Z/4A\\_0__V
+MT.HUQ\8=."IA.1MIS]LRQ(6XG/*R#Z4E?S0D=P?BP%D)83FE2Q8\]#/,:3)$
+MR@L:FUG20.A74KA^^7(Z<F7M_`_[G&Q4;*0[^]NR@!W6*+@%\3_8RL(*NCU#
+M,C;3_8UF%E[VH3FP9OQP?S,7'#^WE,,7UP<6T1\D`12ICTWW2YS&A>_>)N8@
+MD'0'D;LOG\0Z,3CP#=T'4:O+^VCSH[;$.J\U\&E6]2#UXO<R$N/=2]V(W5(:
+M\XF2=OWC<'?#69+MPSA]N(?S@[C.$67YN.?R-0H\YFXSGXHIW0?=[1<#S?F#
+M'V[62Y[/ASW<;-#1M[@5;N^YX6)_*MT(QW*0VC."=/T^<MQ(Y_YBJ1'KP+SU
+M)2];#@>*G/;#:9<^B:6-U_#7=`AR<W&J>42]L&[1K9\]5O!+>84#O;&NN872
+M+@;M#Z33?1+[P^?A5';#4B;=P;18.UV_&&&3&?WIRH+=;7H=MJO+:^1T^YIL
+M$NI&F8>#3%;KQBAH,TK>.F1&L@O+;I:#K_W#7[O5]/@#ENI&:<:+?;1/G?>?
+M+PWS@W]/JN*=3@.O)N;!OQ>G5/'F]=W5[<)X/OCWXESQJ(HOKK^XOGO%^;E_
+MZ^_%:534F]&Y'3!>7S[526B#CCNK)X5/BS*<2U9/_/:<ZNT]QW-[CN?V','M
+M.69[NJVG(9K6D=B%8Z]3.]J:,ZH[G5'=ZQCJK).FHPZ3MO.B.1*:4Y\YV)D#
+MFCF?.>H(YJQ3EH=VD')`!`$9U$.//>?.Z02S.M*(%.IAR=-WD>/?$J?)Y=4A
+MSX-.A,XZ/KJ>-3V[ACJ96L=8<_AUT@'9P^)8[2SA)>1:S_7V]3CPZK-G!)O=
+MZF3RS.GE''6N<]$3AZC;V>O#_(L8=:0ZSWW@]/=.9\5SP/SR+/JHONAH^_2T
+MU(:G%^4UH87^%LU8WZDN`%-?U54[HURWU<.MI^,3=&!@7V6IUIQ.Y7-N<0I]
+MQ1T3I3/DLQT&WPFED?/>P:@@&H30`D"S8#FVZNF7SCGWG',^#:27Y`3.U,XD
+MK_#L=;1X$CKM@'`[Z!MX"IV<UIV$S7)P).)\,/==/6(N<&1A1&:1WD;TLT+3
+M<VRYYY#S"5FH?N+LP\3PD_33CG!W@N@@A!8!-`F?'%)O1]N#S1F:\TBS1%"Q
+M&`3%-"-QQNY015:08>$$<N$OS?BKB2@!L`>`PXS`>5@"H!,`ZVO[(BOF4G^#
+M:3LB03MH7?+H=50EIZ0[9L5IGIW7J9%.-.1JDX!1A.F$1*-+(1!J[(T9@Y#G
+MH,2)4`N@9"X]0PP,,:!E.M1^L-/I,^`+G$#?"7U9Z`M0(\?!@SZ!KQX*7X]:
+M#!SG&3F&$]D)>IV`-J$):56@)7!F,!/*(CSHA+*)]9C-BJ_I%UNZ6C=+].*\
+M828]$6325Q*Z6*\(:U-!`RSH0.$X4:%N!MT\HY7T)C"@7,)<%.:B(`?B(I0G
+MP$T$0<^BD"QEDBH($VI%C3@==.HX)U48FXRV41TTY4"J&,Z+XH8&,)&Z*,Z`
+M)JISPEL0W)+0UL-R9H(C+%<PPH-980?1P713`;%5L'GCN@ZX]>`M+\YU<;MK
+MY46\C&N0<156HOHKO(GIG(C.`3^,L!?J@HQO%.BR0-=C>SM1F\/V.LAMG+?<
+M,0<9NY.9C,R(,^#-D&\VHXBG43@RMMBLBN8*B$NP7`!Q3H@;A+@BQ"4A;GJ?
+M2%D2Z;%$(#60.:]596AD9YQ!5OU)0MFT)PK/1^Z?6[A_#,QA5>3U#7A]>4:<
+M*"T(DY*\*$Y0PZ`&`2P+7P-<%HW+>N#E%O#"M1&JL*$<&6PG#@M1;D94C8HQ
+M<A&S$L%JI+$*)\@LPF%)</+S\H:(-@F:2:HD0'7"41:'3<#0"#.><0-1,J3)
+MXHK%!*8`AT%BDPW$P,ES<\)4Y:ZZ=`((-/WRTCHX*S\%F%ZB,+*K/>U%BW)Q
+MA1M82NZ8$WB"L),%G0$[6`,'X0P6</CC'O%XL]B8;(^T/;0L\YAG%-45GHJC
+M03B*PD\0.3DAQ`L@<>\<YX*9FV@(1S?R%-%,#S(&'"HO\^9A&N\7V$BX&@ED
+M)5@PT9!8IN!#99RHN,!'T'S)VO4R=EDPB8))@'.:J=,,F5<?97R2M*`(9Y5C
+MG'GP9L^*8<.&!=[T>0]FX<M&*P*ZX2/)/Q(.G;P_SPW>X=2V*8=^/(%7G>@>
+MT^1P<7I<G*RYKB;FHAV,[3I"O$Z6Q0([YAJ3$C$E0<H?Y@F>999P/I+%,5(P
+MZ;QFUR:W1_<=<YKF.:U3J2G)$+;:JE.:<5]L3@OVQ&*TP7QDI_D,YIW(A\.<
+M`C'U!B('S*ZSH!\K[_##Y=22!ACD?A3-9D"!L0&8@(PST>%#H)MU<57\G4`9
+M5B;0N3#LG8SN%^>I!Z8S<K`\XHJ07))/E:3H%H\3CLD':+,GY=&D22&#YBY)
+M'8MF;C#69NX<^NAL]GK-WF3[;?:P9@4O6A:_6&1M7B9N9B^U)+!^*OV7VT9E
+MHGMG^N1PLIU@12SB9)T\U"^.+3+0`X''K&]S'"`/L&@:/7D88HQ`@-MCGWOL
+M,W99YYG+V4/7(X^.2F%(*:63F<G-"_I%L9(S0ZRH2CR,EC:7KZ#8(*SE5DAZ
+MB&ACBR9D^HJ\E")J*R"YX"\5"*N`9,VFQP![,[S]4_.7[,A$\93Z*L35D?2R
+MYTX&WD&)#H?%P1&>!W@S[;@'C5I[''@/.`->1<`(!3C.<FM2^R"UERZ:_606
+MR4MHTI*FJD@/!YPI#*;#.7?5*Z^X30`H&W?A'V=<]ZP>93THR\+7F2CS1%2O
+MHZ"-!<^U!S^65[&S(*MZJ-..=(GR2\&,F`?07N/R6!+S;%&M0-0@H7:DA`**
+M4E/3M0Y3&=5NE$,3I6Q1>(Z03BR6$C+O@BRLA-U)V%FR#M(*8NXD\NLMZ('^
+M)LF37<BP;@9W+7_CI!^*MT1R.!RN)0\EG%[NFNZ3[EJF`6L),>ZTG0E%8AZ=
+MI7LL-4B2R)$(<+"'P\?QG:6D92&]N+VR8)+[$36IP:*6H&1N9;K*;SJXFWS1
+MR'3*V3'_+:V,M4LP3R+:D3)89)%EGB*^(]-`QL,92?4MTUMP,PJ2E(M`%BTT
+M1\\2@32+I0#83F-UBC_=(-=%-@-IQ1;;H9J>#GF+^LD+!5G54#N&"^"0Z<1&
+MYG9W@KJ7;R9JCQ)-E/V+/9K>[`*TE["R"3")6R+..)8_"?*1A+DS1SH@V4$0
+MS4]M$M"`"B2UY-228\G"(2HY$Q8>(_[FY[)VX4E@>>QZZ-#T@((O#VB_FA59
+MJ;($7!*>7*-9@Z'EN2U`R79XK62N(%6H&W!'>RP??DR$;P<@UC=SYV4[\(91
+M2%LN@*:<](]T?D:-![27K`/$2Z;!68K!MX4CUH=L<2"0.8Y8O`CS1\`;95>4
+M11>FTB)GU$-'&91EC&O&N\M05?/?F;,BTBF*YG`4D-=3B5[1C".(<5A.A^5T
+M6$=;K!.U><OU&<0*$"/=-^FI6%\I98DGRLAG`JD)9[9&9BMB$$F2;Y2TV)-P
+M@A,SEC6K\IA[#`@+`=-2$@`L2N08=F11!$/SLHC:43B28I*M`Q0.FZN,@^"2
+MD$^PU1Y;:`P"3C2G)^(-1408L2[FFR86#K1XY\S![%K^FE"">%Q]*;+IA58M
+M7$5!L>P.[FT!&N-P9/J=C).MA+$"YN1U>'E(#0<D/SW)V6#B-X\0MIRV7%0=
+M,G0D'(*LK'_+F-HRBZ4UE9@I8+<(;(6H7<*R8)&F4)\Z*I*96+J6N<R6@Q0B
+MM<)GZ6Z-"7/N6$%V0TM<1RQSS*R#R+],6`GYB"U?@R7+MCYEJ(;+^\XZAT&4
+M#9/RJ&VYH6(")^_-*3YP%GHZO$!G"T:6+*,['M%[P;E.=\3_QZ[8JI%;R`-K
+M%.6I15NUEG%&',V#Z;%29D-8#B1:;ZB0V<SDN$%;4;;!$Q$;Y;$H3'*)Q3<"
+M7-N2@+6QQ;7`BE8`!W*MHJ0>];GY%?ATB6XEU"^99C:H`\#,`S.>1B854B0T
+M\Y<EH<(*C<)(T6"Q`-$6?S#K3GUV1!`M@(=E&YWA$P<8>31_NVKLR,1$S4#B
+M20FJE9?@\!)"H[I,%K1%T**6K-7&MNA&I+78/J`Q]E(DV,ARQ+8:18@E/\*S
+MPNX4K@<I43#C!'U%XN!H,T=7;95;6B'<LF!HFR0BSE]IF;?>HC\41CZ"N$U,
+MAK@=7.9HP.$GV/PY9MP65>3E-NVD&P%E4/I,$UP$:!QG-K1,)"9ZB19)69J$
+M-7Z8WW++(H="K%*P005_-+%$CQ-,<HEH"I[U&"O;8A'PG&S/SCA!MS-LK:\L
+M^H$Z>1%)$$NXD9G)SN0'L[D'!6Q9P@9QPT6VBFKHPK7W>K#',`6VLV"2;)5]
+M%/2K*FB3C44B<F`RLY<MRY'I3,%T%S&0V41];D$;0;FSM`[I:4FT+7"0_+4-
+M#[8@S:J!=#3S%.(>UIL\5IHM%TZYY/)4%L39?@49QQ;$#2`M$I1:^(8A[0A1
+MB)2342A?95D^DJV^^4B%[!5Y2<<6J>;S"2J.'6;R'90"2^IVD;MC^2_?ME1$
+M7(26:B9DB_*SI%1AL79`'Q-10%,ON99%3VR;$30;\A!*HWQB(@L,"0B\K;(3
+MM042*=)`9J%;,"+(T7:AME!)LQEMS9B`3-ZU6&:YH"4%(]G;1AD!4+,LL&D0
+MBGD6^[5PH#T9"0]@`IY54+0<A+Z`P99"1MOW$C%UT=*]NC<!BBS)9Z17>$PA
+M#6OKXMA41UW'/'KY(1X;[XGS`B8OL,(<+!,96&^;]O?.(Y+P;3-$,@Y#'!G'
+M*D,.G<Q>401<+-;'"N-M2ABF*9"2;:E;I,((IW&6;>.0):G)0-H2.D-.+&(F
+MV[65\6\M7=4,B4!:B+(TW3*[CM1@@HT<VU.TC<RS<<[V(5F^DW$ED7FRI[/P
+M)H';4S1LGM%2Z3!`<R0);#V9L`!S1GR6MOQD_@8>D2WT$K%8PMJKN:+5!G:_
+M8&_QAPAG:*!9*EA7/7-Y@;1A]WIF*1QXQ:"P,0DC=@O(^EHR!PVPU5TM<EH\
+MC8X$G-S(3E?A)3LPEH$2?K_E*?G"X2;:_A./[VWI'07L62H7Q-I1RD12C!2Z
+M6ZXP(<M$0$:T'J1PC:$SKKCXS-BAX-\76L-<]YI(BT4]:NT):E'-%FH1G;4U
+M8@U#N`@\QRTV;K#$+D)T6C'RA(-MNQ;A8#0+&FVBE/ZQ#`[1.Y$M:I61GX4V
+M!"RL9V*_7,(GR(B?C%`O2T/4YWQ;)A6E#=@.F$:Z:1;>=HW0,TO("Y7D)-CR
+MZN)BRRMN&UNQ;4&CL*9M&3;(S].QB`/4<AVLN;.%VHP^$&;:`L3!QH1NL91M
+M"62Y5*SXLKO4MB<'`A=VQ9&'9%ERDB.D56QH%FB@`F@<^9=`YCE89!8L><FD
+MLA8@:F+75E+@9<;5^#85]H?8AKJ"@U'DT13QN4)/BY9LT<W4R&-6/!&-)R7G
+M!7U3J!:L$X-B%@4NCV?J+=UFN2U+IV.4(N)O`"#(3$Q=PAX8A8AC+<?5-L9;
+M,I=-4[9LB&_#BHUMKDU,*DM!;/12X[;L3V*<ARD^=I:U[5J*P_8?6:[*(G5[
+MCP0D@H^A[M,D)>Y8S/6L+[K>=JRUC8UTT[;9VYJG;04(BE78.F\>AGJG/D53
+MU$+"LCF='9DB-H_"-J8!MFAHJ5/EC#RZD0A82>^((D)CP$+$X[2OQ#9!8'S8
+MBT62))KU4W1,:LNB8TOR=+LOJF,U+^:$MG<%TX-_Y4%A:NM:&:%9=I.=W#A>
+MT1A$HN&E%5O6B0LE50A1A$MP"G",*PFJ-)UX8^C?9%E)<UNL@ZPLE"(D!=K-
+MO+"9D%#.V`I;&*"+EE,0E49U/>$P:RF][8IA_8-5<=*1%GZ:$\XV3W+4MK_"
+M]!<)1M2/I0Q+?M@"',99GE%N(2ST:3E\<:,7ER%;L^&-\<WYL->\;`,6H2N)
+MSY9X,`\)@&FFQ$%M<0&4V>YS3U@2L!$M*<9NXL#>FX0.)$C-MA<49%U$FI:9
+ML<0!6W@*`U**TZ6VI-[V#6M.6^!NV4H;#%O#>.$@6X!F6HO<21`(.K8^"A-%
+M/**D&<,JL]8;;$,(KXP5,D[!7&=10M1?>]E+?I%+S4&V5P3(_;%!1SUN&V5L
+MW+9DZ^B:Y9NDXZ2EB9J88UO3(M)R]D*=[>EM\K7E+MPTND+(8*Z3GF.AK=:.
+MM0.Y;QE?>0@!D`1;;K,7#7E["J>RA;21%PUY]<LPPKA--?%GQ!QMZ;*'B"PW
+MP,J]O5%HKP9JP99=N;9BG4@[V99VSP*K;C$B;/ABUP54Q?.#+7@9T!Q!9Y;X
+M;,81N&TWMGTA;%7%-$3;>FCK<K8U`>_(-L@'WN0D@K=F9,BSV2F+HG6+A3OD
+M38HHQS$Y60(V2QL-:H$0T';D>U;&;)N`O?_UY">?/OGII_\R_<Q(_2F7\:8W
+M?_KM=]_]Y[>7__[7R]MW__'7R[M_O?ST#W_ZY@]__J]O?JO;+B[_#PQUTQ:@
+#^0``
+`
+end
diff --git a/usr.sbin/pcvt/demo/playvt.c b/usr.sbin/pcvt/demo/playvt.c
new file mode 100644
index 0000000..17eeca9
--- /dev/null
+++ b/usr.sbin/pcvt/demo/playvt.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1995 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Hellmuth Michaelis
+ * 4. The name 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 ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, 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
+#if 0
+static char *id =
+ "@(#)playvt.c, 1.00, Last Edit-Date: [Sun Jan 1 18:32:22 1995]";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm want to see my xmas greeting ... :-)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ int c;
+ FILE *fp = stdin;
+ volatile int i;
+ int delay = 0;
+ int fflag = -1;
+ char *filename;
+
+ while( (c = getopt(argc, argv, "d:f:")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ delay = atoi(optarg);
+ break;
+
+ case 'f':
+ filename = optarg;
+ fflag = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(fflag == 1)
+ {
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening file ");
+ strcat(buffer,filename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ while((c = getc(fp)) != EOF)
+ {
+ putchar(c);
+ for(i = delay; i > 0; i--)
+ ;
+ }
+}
+
+
+usage()
+{
+ fprintf(stderr,"\nplayvt - play a VT animation with programmable delay\n");
+ fprintf(stderr,"usage: playvt -f [filename] -d [delay]\n");
+ fprintf(stderr," -f <filename> file containing the animation\n");
+ fprintf(stderr," -d <delay> delay between characters\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/demo/sgr.vt.gz.uu b/usr.sbin/pcvt/demo/sgr.vt.gz.uu
new file mode 100644
index 0000000..b2f0eef
--- /dev/null
+++ b/usr.sbin/pcvt/demo/sgr.vt.gz.uu
@@ -0,0 +1,12 @@
+# $FreeBSD$
+begin 644 sgr.vt.gz
+M'XL("):V^BX"`W-G<BYV=`"%44U/@T`0/2]_H9<Y:AH;MMT5#?&`L*D8^Q&H
+MIYYJNU0BA0:HO]]AV18L&"=D=LE[,^\]&*Q-VXP&ZU=CL#8/)!1OPEW!-'"6
+M+[X+@9A[_LI?S,$3L\4\7`6.>KOQA!M.`W@"$;JP+&Q\1B/L<+@U#"**[>8H
+M"0ED<4K*.-W#/M\</^,MY#+=Q66<I0:Y4T7TV5NU)Y3`0UV)DR3@E&4>?YQ*
+M64`610V'(H<BAP(\9\FN`1@"#($QP'NZDWD2I[)!.:(<T0F.(?*%=AO00M!"
+MD`$$\EOF16N0VM7BJA->2PY[UE.;*Q9*W&M65X;:EB*AE*5)'3FF%E6=/+2"
+M]&QC:EO5R6.;V5G)%;'JA)J7],.^I#J%DJ?T*FUO(J8S*1]T?#W2(\+U1&UH
+F\OMC=0<L[<K2KMCY%_UKC%^LU5+\KSQGT6K:P/H!L`MLDRX#``#2
+`
+end
diff --git a/usr.sbin/pcvt/demo/twzone.vt.gz.uu b/usr.sbin/pcvt/demo/twzone.vt.gz.uu
new file mode 100644
index 0000000..df05d50
--- /dev/null
+++ b/usr.sbin/pcvt/demo/twzone.vt.gz.uu
@@ -0,0 +1,350 @@
+begin 644 twzone.vt.gz
+M'XL("`PIFB8"`W1W>F]N92YV=`#M?5MS'<>1YN-&G-@`?H!>VK$7CV-WI*Y[
+MMV%Z!Y8@'U@2Y1$XJ_!X7R2*MD00D"%1XO'^^NW.S"_KTET@'V;?K`@%#[JK
+MJ[*R\EY96?_Y\-X__>Z]7XV']_YLS(5/Q^&]/UMS8?WZ(UP8M_YKPH4-]&.Z
+MX`?V(DS<@AJZBS`>AZ6/Z<+2^Z4':FCMA9_IB;\(^-30-V:^,./Z8[YPHW0>
+M(_6R_+)1&GL>>)2.XT6DQOXB"62.81[I8^[64B_+0(#52I,`Z!B69:*&H$L"
+M0I)I+="B$WLQR:N8Y)6=I3N>FKN(CO^=91K>\[PL?3G>K?W\84'RV?+_?QK^
+M8_\[_$?W]P\`_P'@/P#\!X#_GP%<1?A\7,3EW=4J(DE.Q_GX0#\6D?M`+29J
+M\86V"%%:+`*<6P1J\4Q;I"`M'/J(U.*8^\`H(4F+U,!AC;2PZ&/1!FN3&VWB
+M+9J@$V.HR77N9<(X$YIX:G*I39P#L&B1FDX6;2?CS&C"./E8FQ@T"=)B@;J>
+ML0?6G'82FTX<@%T4KS296\RB24*31<?7T"ZZF)MH"Y[Q9QDI7EHL.E*:\#B?
+MY_7!"EJ=4&JPGS!E9]%D:D`Q6&5GT"0TRYP`K=%>>)D_RK``W(A>'"/W:4:N
+M$BTZB4T+"UHQC)9U:(-?7G\EI\]&_`KY[:S/K+;3E_@1)_PR07]ILZ#]6ATK
+MZ5NOWT9]ZW(O>0R=A<_M\K/<+O_2,4R&+V-`Q]`I)IVB_HCZR^11M=_(?:P(
+M7QE^78`5BR=A[RNV;O%@7A_,^>]P?+;^HW]/QR_(.-0'"X$LQ&=2\<0<%UJS
+MKGCBCY=DJ.8GT_%CM@?Q9.',(QO%VB:M_9@"F(5(/F;K4I_,*WRF`'"AJ(]`
+M*?>@_Q6>HLE\_'RU2?7!0K@+2<:B15P?%'-:&&1%5/'-PJV?D9&=G[AUG/)!
+M6E&C<S0RP#DOQ+>$HP66#P31KPDF_)V.+]BI\/)@I@=FY8H/9"E^H.&2_+VL
+MQ(_K/!=2_@`+\1U-*^*!/W[%ZV#Q)*U-R"S_``OS%UH&I[W$]<D"O--NYA76
+M!>F+9/P`B_<M$<'"%!\`I?>T5`Y#+2A\02ATF.*"GQ\)R_AHP>!W1&W:KS_>
+M$3DZ/(AKMPM-.'UBC]^L)!V`EF6YOX/W\P'6^WLB6H<VL11WQ.CO$[*G]5^W
+M,M3[3&?\@B!ZGY"P"'AN:B=YM7Y<:KE!A)M;L?P^+3)_M/Q8]-'[!#Y_;4D!
+MOD^PQ4G&M%&&6K#[?F$+0&C&=1G7I@+FBG4&,\BW*Q.M_R9,+/'?;I5/[Q?&
+M`Q3+:CMPCX`/'7I`)6-%0#ZNB.>VBT4B"!@K%>%H!M26$>"HZ3J>C&,RYGGZ
+MAEQ5QI4#7EDW_4YZ32M!O@\?64#A[A9N`E8C'HR"WH257+CD_<*R^9/0@%MI
+MC7`@*^+0";G&[Q/58*V<E1\&O4;%0:R((`BT"Y#XF!'HI=?5^<<BR;J"<%*!
+MV=J4`RCK#RP8?H!0A29`<RNPDR`]`VNK;I-,;`9('@0["4'(,!X+""2MI`V\
+MUI;<2MG49`(/34!L7(4$@Z9<X"(H&908M%]7FFY!\+>.3!^'5972O[+41@:,
+MJ\H4GE6&1*=BU6"YIE5X,@#<VR3_6@L,3\")EU<K3XP\<@#'BIGY(7<;\XQY
+M95>R`.\JR^GJ6^T?C;V":RKS=5P-3UHP:2H@1="'DQ5=&4QH2E?!`U@QK+^$
+M,$B"1_XBK!:E\($NEX=4P')EAH:$$5O\J&3@519YACD!!>!:D#UWMF`"7*QX
+M%</[6KDK"L%&95HAAJ3<-?(Z!L@B@P6UNEJ^M*'76)A0.__K!<M61DE@W2@T
+MD;*HT.G7]G]`BP1=,%Z`%@4O8S'M")V`M4.GH:36E8Z%PH-RRRB"7G#L%<=X
+M(-(G0KK:6A5XD%^"%K0RRJITA2<F0&JQ8A"(#J*E\054\GL0)JNJ&4SDA5(C
+M6D:9_:JKI,M*OUH+]6+DQX*H!,),8.,LR70<89&@&&"1_8E2JJI3[0W:.N)-
+M%+X+&!FKJ4I;'+.G*EF4T:U@406*8EQLBB0#3D+%+O.J>(3@U5F$WZK@+,A(
+M=+VL&D1!U,5*@%75B_,59D>H"B\P6BL(!62K72<\DK0%-!"HU=7:9<*B./EV
+MU87@"LA'-3,"NE4BQ'(Y5Q%LPA*K%@'1*Q\[\/P(VR$*I<1L$;EZO4*6QTY5
+M'*3=)%RF3*UH\I`8BMI8:4,+V6DOP.1J1X%5$\P:KV]&,1D4V%#%#@)W%K`&
+M7E"APD7E(6Q1`WLG0KBR?QB.OV/@H#:<=)0@I=0\<BIS8C:`!S*G,;4$ZR&I
+M)H-N`7\ZJ+9(#I9JFQG*.G<BO!9T<J$P/#]BF:J$I30!RQ=<$"#,5*E,JS=H
+M0I;,(D7`H!Z]`2LF*^1Y]1-#EMAL&\R0D!8T,T+PI3QN.OZ)[0FP5`#2)[4`
+M0&#:QL=L9EW2\@&U`4,ZR!7U53PZSE\[\K9=]C:BF.B*;B5ES"T;#'9UNJ,T
+MQ:).JGR41!+X+]IL'M-VD*(VP:8`>B90?\`395%#P8C53(4245$6P>!J^4,,
+M9F,W';_D-Q!N3F1:4.,%+)C='I\M#PHB)&BZ*.A1?C;Z1EEMS.;@ASQ)C*3V
+M61+<J1@5T4N"%B8J[^Y9]4?$H(:!IK:_*KM06&%']AE!_T;L(2%93!7&C;49
+MWXNUR1]#WGE11VH?`9/JR[C"_KEF)QB.':\T)"^LVTDT"JM46#FR/:H\JT:L
+MR*RDLP'/JONQ\.H-P2:<*&P!=P$^[DK/8K3[;+(\)32JO:.R**G]+IU@^=0[
+M<08;G["50)K*EU8-3[5P?%;K7[*X55,@\DP]L*2^(=2\S>;+)\Q*F.&$3HR@
+M7`,%22A&)>=BI3S%QK"(7=AE(%6U+=5SR(M,HE/%U`0CRZF-+^QH@%-_8;/-
+M07O5F<T5Z[`!G,Q=G:9L73J2V7,.T!C1@)/:&&J.H#N5!$[DEX?_%J`V`H2/
+M4UM<M%`H[(2GO`#B#'C(:+62H=NLNDF%-7#)8@IM#)2:A2<G'K:'U9'I,U#$
+M,5O=RNX60D]M9XE8J-$T4]@,S+8J4UCN7K71WYGP5,>IW+>J*+]G`:I8PNJJ
+MR(AKC,^!85UV<[63A2:_886H/A-B0,K#X?@UJW.1A=F*81DQ9P\L@=9=H1B_
+M8AH$_$J#8]9^WS(EJQ,&8[G0-:^)=<"T"7C)NLQ2&#0@(*`AB1&JTL1,S`$Z
+M/;N(X_$Y<Y@&/<#L8Q;?WY)F!0*<<&<JM-H;0AD(5LT,'@8ZT@"XPN\Q@@4#
+M9:P&9UZM>0VFIAP#4#M2NC$N,X"&X91/%P[XD<#58*G&4:>L*+[CP2';H0E=
+M(>%N264)BZA9D<78O&)A-6>$*354E[&=B`5"X7:I<66S//N>T0Q=:!"T*!U;
+M[B>II(>&$W]J74C8V-I/(7M>LUC2P*_&YVR6;@Q&1'@!-@'W8JUX4$":8=\$
+M(N8K'@"KH1'5[)0P+K)=&,$BV24,1_97+*)`2EACX2]0WD\A6CEMR>;EG427
+MYS">.`DZ4D3>4>9^RBGR678-+(H+]J4<HE2P/"="Z3<F(/-IJBS/U34HHI]L
+M6!6!,'Y@BGC;P&9:8XVYBX*+>:0<K)6\J<Q$D\S`^,P7`U'TF+E`)EE;068N
+MC(0D;5QC=!2XX]RP.1OZ=I(YJ?4N&66^B'\!P[YPW0F^K/4\4LH*-WP@FK29
+MJ#@7K*!$SODJ-1@GQO$WJ]G!C=7H5#C7A8(,M1Z_!#L.2V\X^LOM%5A9B96E
+M(@82]6!I%W_]E;2/"0.L9B1_.@&C)H*F[(BI&B<48[$V9LZSED96;4-KP3(F
+M02&;I`,8=!)TZ@Y3#YC3JFLG/(,.%,",SZ0GB+7J/ABB6/DEJ#)&/U7]OW(*
+MFNF<!%B+YHA1$\-]1FLM?\YL!RY4?+.V=V+]B825KB%Q$4C!_I"!`H+8U;]E
+M]P9\4[A`#BW$B'?0SA;V,38[)-XY'6\(QT;\+2@*B1\;D;16#"=+OH#':T%^
+M]IZ<>$+XVXJWX<2L<F*.PL447DN*`NPVJ0GEQ*DS,B.!>^G@DCJ.F>]NF&RX
+M'<(XB"KB7P=$.-&:0N2ZE0,4&MDK-!)_-9@"5+X\=R!ZB_`:(JE&HI56D`+4
+M6VRG"DX7X_=`(Z)G(^%K^#KH%XOI,)+@1$2W,;`K1XFBR=\D]Z]H7<3[%WIU
+MR'.`0!C587"(%,DT$7:R<+)%T?+>OHU95]P0"0-HH`_8PAZMS-U2!@3H(`KV
+MX>.0@/TC+9NPA)`G-N2L.,G"S`B!K`KXFG8*9?I$+5;6#AD(I/R>$N8A/P5_
+M%UA!D+]$=+`PX!;=.C20548`$S@E/@434)UK,8!TFN'XC-$/AA%)881T8&TX
+M`0`36=#U(7]7Z+X;`D36"98QUD^(3#D8`ZD,=`#58O)@22%G`R2&X_\6-7Y-
+M'AKWD#TE!/V,]BBSD<@Q8`!E81$P,,2A@2&)D1%\=;(LZ+:P5Y]1!\+"L`H-
+MY\!D@8K`.`">P`4J-<!ZD&."/6S*6,@]B2A,6:-S9@]<-0>!+/0-&8`.,%=!
+M0D!F`E`$B:^H``6*:P0Z\U@$R$\KDM;(O^!+Z""5$!R8,K!DC&)7!`*9`*Q-
+MD6>_;CR-GV1K=365:#/J$WUN):Y9-(4UV38-;%L5+9V8K_(H&\ZQZG`6ZZEX
+MQ/;VI,]*PS9M1F;;VTS5B]+0M2548NJ6C\22';<]2URW_'R4<Q+%YVSEUC.8
+MQ?XOGWFQ:S?#B(U9=<"F?H4\*Z<,RG9L4/OZ&<YP;!$R\4AEXQE>1OE0/*#R
+M$=N]53,GQSE*(!U.7%0-#8*?S=3%WG8MJ,8W3<V\]^W-'R\_O$(+LNA^A3S%
+M_XET.0=J?PVP?LL_'#4FL_%#I`O^^P$[(;]`UN^O]=$K]/1_\-V?T>@%AOT.
+M@WR$03Y`WQG(OP&VVP->6O3T=[Q[C6%_C;[O\/TK#/('`/(;S2W]5XP;-;7T
+M&=[]6K,BT?=/&/82/?T[!GF.0?YR0)>_`"2_48-[1`=O\.X%AOT"P_Y"(3GB
+M,X//;M'HO^'[;S35]"]H]#<T^A=\;]'COVKKO^*=:T`:*:I*W_]WA=OCT7]%
+MH]_CL[^BQV^U=4"CJW*=ZPGDOB,:O<2[7Z'U2=?IG_'H%3Y+^.SV@'?_%^\F
+M?/8_T.B5]C2@T=P0BCU^I]BYWRR8(O67VM,3M/Z^1"$OV*%$3[D&[O@W??=-
+MLP8YP=D>?\:C'W0-%+H?&T3;X]?Z[C4>_:3X/6S83M\E_>[G#<"3?O=R@[(W
+M6XJ^P[N3]OGW%ATN+Y'31U]M6WV];?6\:L48/53-FF=-NVE-#K6LBQ/U1C+\
+MN1B:5A*>GW.+>7U`Z=U6;#A+ALHB1*]8W(:U*05)K7@03Q#!Y-UXSTUIR^0)
+M8J3V@)V1Y^(@7(EL?B(&_7-DC4IWB8`FK_Z*80O')^P]A+4->1&4P.J<S"92
+M)_/Z[179:XDZHZB=%4/\3D7I$S:$9H'$RA:\C\`%NB=V#CDH&+@7LO]X-H0U
+MSBJ[$QOU"5*^N0]B&G)T.'-E0?3=05P>SCSRA`J.EUR)37^%_(HG2(BW\&JO
+M:.!E00^0OW?86[G#+NYSR>*VW"_!X%9TW5'_LZ!KF8^E`,?R1++59@R=CG>(
+M`UTAQ^HYSY4^YVVC)SRT86C""N@=#D(*"3C!#!&`I7,ZSY$4\80&IT6SE,M\
+M=T#^K14'UF)GX0IFP!713>",?<>DMG;/+9?N[4%L0\M3X23V:9W3$^BUYP03
+M.J/)4OCB"0*+S]E3<#,>>5F"94(66057*C">77]V]>G5S8V:SI<W*G:OGWY\
+M_?3ZV9_`V)=HM;BM3S\:#B#']0^L.[7AM:.?1(7%((P3^8(8G'LEA!?MB+XQ
+MRHJE#`RO('U&_%Z`2>M?3HE6BYJ2S."QB%-*F&B+$7]K#)"!)/(K&I,@R4,2
+MN>8_B<?KKD/QFL223)Y6!*^4"DO@22YP:Y(90*C,B)BS:$]"BIN3H"R!=*F>
+M(!$Z8X,$5EZ0P(^)J'@QB;L4:*>K1Q*TPHQ+6#+F%ZR1E:&(Q]%IJ`B,!$(Y
+M^3$3(Y$L#TI"!F1!+,+/O>*'>4VP1KQ9+P?6E06GHB"5P)"T*_'%BD!INV8;
+MDJ+U.@2=NGQ%HEWHT$V9R8@D$2&ZQ,3'E@RF3+F^@-\5\Q0<D^3663GIGM4@
+M8`)Z6"9>"LPICS$7R""Q>RFL&US)AR2[2S218JD;9#$0L_PH68+EFR!I6;GB
+M#<G3_#>)U8)A2/S7"Q%,#7FU+JS7*VD1QKI_P1%K#?UI*S8=%2$LD<$N($6V
+M"03OE*S`Q_%ASX91_$TO'F*`ESR)QZN!C)FBX"N<CN,&R\@WLO4P@$Y5J`KQ
+M!(E$8`-MK0,`BVB0\TW,,;.X]Q:;?D%"("%*BX0M(<?5#(),9]V),-@"0RI-
+ME,U$=I2C],]"_D:K%Y`\9R#731>G$0*!`:4)!$U6P)VP/S6B%V(992/BK(#(
+MC'P\"BZ=S)$3*08DC3_EL%%"+P[3EWTO&1&(LK*&O+<YX%3C@'VP0:)3`_(E
+M!N1VLMJ097:Z,"CWL*#[V4$X$P*S+,O`A,UKEX"5>/R4%V26]4Y'/HHLTP"2
+MC>"6M,6E''@;D/\`A<)KNDZ<`)%/';RP!+Q93%W)-.C$O$9GA%IL\3EH@2<]
+MR]K-F0B#QIK`3M`C*(;!RV*5@Q*P,0D-&BR5+K-1DHOX*@*INF8!A(]B&GP6
+M9<!IGP%;>])11!!/&=C+VLN_'@5#@NQ\>P`LY#*AB7#8)/0Y`6$C(/$";<Q2
+M`;RHJX0E#[)ABU"5P[A@E!&L/M)Z"$,GP.9!:]BXEE<.2S`*PB>,S$+L('IU
+M$!&*HYB#G$0=1!1?2I[4`"'*\PK"02)L1LB"<1W_3\0.4IQ$"`S,["%3$VJV
+M<-K^(-GZ`CL60^045LD5-()(H2S/++!X&8GV,SY"YJ3J!XT&2G=&)B2P)*`M
+MRJL$R@N%<#82)$Y"_BJ#K(`2-&D`BF0$"%K`1G,R%',2#G4S9/^,MLA_"!Q8
+MCP6O(]RKA(!B-EZH92Q*X(0CC&$U(AEEVAW4V00$!:4Y!$>#%*2!TG*HS9.9
+M7J.X3E.1Q2>P`BY6(JA2">!951`)V1DF)[B(J$,RBG(Z`VP1PK69OR'@=-4B
+MECKC!0(ZRQ19-X^]B`"QXU$]"+(%:EQ+`:$XD0<+8[:0!U'(>5W0Q:HZR![U
+M()X(]B0&G)`44E))/6N&AV;UB%\G#)2*'Y")7M$Z@_\@B+'HS@EZG===D`%'
+M5`9D]LOLE?%'S<09X'->ZXG^&S8RH"(C$*2EBG2-Q1I0^2S+Q#DG:M.IJ0;S
+M7'V=`0;_(/N+/(,9O40H8VSZP(A)N3C3QSS'62@'8MU-8KMDX$2XN4GW.X0%
+M\$((WREYS^``9%X+4Q>4JFN&M"2U0B:99QQ%P'CL)7G*$IFQ0E:C4@&2$WL_
+MJJUF2'\(0:_:1"U4H`^@I5P.BZ)4L!:TK!9D'OOIES1W_8AR@5V!ZI%W>0.F
+M$V#I94*#%H:)@:0KV`HC$.MAS,$PFK`62OI)9H.$M074I_"O!N01#0=X[X,<
+M]QOD6/P`/U_ZU>51LWK,65@@6H';04A.NGDYB&\_X!C!@+,E3)BR/F/I2`UB
+M6J;-D[AY$HHG!QRQ+QN)1WJCK]WFM2M?V\UK6[XVF]>F?#UN7I.7IGZ".GOR
+M0>&-R@?LN@TH(;/Y8-I\,!4?3-L/TN:#5'Q01%T&I&"W[6/1/E;M)[R4L<+F
+MVU!\&]IO0_GM9MDX,C&@^DCS;;FF;K.F'&L94/BG^;9<<+=9<`X&#:AYTWR+
+MEQK^0S\;RN`PQX#".$T_)O=CJGXV).0*$G)CV\^8^QG+?NR&LFQ!679N^M&7
+MM"U=]K,A.%L0G)W:?J;<SU3ULZ%#6]"A36T_*?>3JGXV]&D+^K0M?>I+3A<K
+M^MG0JBUHU;:TJB]IW[_L9T.WMJ!;V]*MOJ2<@K*?#0W;@H9M2\/ZDG(.RGXV
+M]&P+>K8M/=M,S[:B9[NA9UO0LVWIV69ZMA4]VPT]VX*>;4O/-M.SK>C9;.C9
+M%/1L6GHVF9Y-1<]F0\^FH&?3TK/)]&PJ>C8;>C8%/9N6GDVF9U/1L]G0LRGH
+MV;3T;#(]FXJ>S8:>34'/IJ5GD^G95/1L-O1L"GHV+3V;3,^FHF>SH6=3T+-I
+MZ=ED>C85/9L-/9N"GDU+SR;3LZGHV6SHV13T;%IZ-IF>347/9D//IJ!GT]*S
+MR?1L*GK>D'-!S2TQ9UJN2'E#R04AMW2<R;BBX@T1%S3<DG"FX(J`-_1;D&]+
+MO9EX*]K=D&Y!N2WA9KJMR'9K]!5V5]-%)MF*8K=&1*%W6[6;M6XE[#:RKA!U
+MK:3+@JZBKPUY%=35$E>FK;J+MH?<0?.]?EY^W7RLW]:?XLL*\NH[^:S^BC\J
+M0-W[9.\+'61OB'[SG=;]QCMM>TTW+3<-SY`[>8]<R0=)=#Y)QO0IQ^,]2NU9
+M>4'R]$'RYT\B/Q[P`1(5'U@H/$A":M$AY=^?#B(33Z(P'LH1)RW)^2`ITS5`
+M28MZ/B`CO6X@!0:QI6#R2#-#EBL:R@L[\@O?3$6J@&ZQPD@AI2C%]^HY2+U'
+MC]E2\O.)ODAERRC8HOSNTZ'J@@N#A@P]QJ0BG%/NT3/PAFO&%=#S=&(%_4$.
+MIO"G<P.,$YP+,&DS<2G(R0<+3I(R7?1A<]],82:7]E9L1YF)H#=_PG_C@0)L
+MYOQMJI9(>R_&'9D^;5$7]`$ZN^PU7E2S-QDM*(V).64*DA*L47"[ZK8'G,L^
+M5<AW-5HL*(%+I=$P20`-#=4Y4WWJ]-.8(8S\;:P^)3OUOJ(BJ8ZJD\R$&&1T
+M9J=):&&21:$4]9.<E6AFYG:7(%X(2J3H[00JFI1BA2M&Z9-HIR995W,@GYP[
+ME8,X&60&V$GZ=B72`L"FA/F:L:2@J2U(N.8"*1L\"4=-LMB^`A=^0M,W2-LP
+MF+R4=DO1KL*KG!PXB4OW(*;P0]UWJJDRD[PL\B2B@(M;%9U/0B\*L$>QW\P+
+MSK5$5PND282#0=\A0YQ:2.?J04255A(!]^*/EZP7,V!^[%`7ZM"F/*Y0K#$-
+M`WG3ZR,*',*=TP919H1\6];A'H<-&U1U9-"LD/&R3ZAF:];.3CB(\H#S&@4&
+M9FJ!;>@.\!.`K^AUNH!\`S6.(B6)ENX1.)#Q]WOW*,9,D;ZRA34Z*^FTX?^$
+MSETQBC+ZU"Y-Z`D/1CR7#:F6A&7")`6ES2@2*F)V.76`"<+X`IFQ6;N&-*:+
+M4./2T=]<G5"0*;029'KXMQRWE;H^-0S@:S$\7<B<4#0Y]\6DXS&YF)%JYH;-
+MI%IS;$EFJBB4_0)!'9..:Z>0BE&F=A2H/.]WEVX263*A4//8R!(SBI!6,ISR
+M`H6Q7:#0&458"K)E+D".#1U`B\@H>2ZHI9W7!0*A%3NC&&8<DJN7.U?$7B;*
+M,B+8'EFG=N$9!%O(!!=;=/FRO#67FG`5$`8/BOKGH:N7(3C\/I2VD`]".:.6
+MV,[*3LP27PP=I+'@H4"T;Q$]MWC(-#EE0_Z>>2_4RC(40XYLG33XT/+C'K7C
+M,T6%L4-1(O(XIT)`:,6#<\*&&8**MW)9_F+R,`*"Z8V<K>BQ67L4?D\R8MJ9
+MJA;J;WDUM'(GP#)H!"%GB_"<:WEOICQ5VTZ5\10+NI\Z-">7,]#N\JZR'$&-
+MHSA\!HM:K+;%K04=J\#330[UP#`U_+29L9,!]T'B'9N3F+CW6IH?"K2F\#BV
+M`P<,/&\&AM*//>>%S0(+MG89!1[L'7M&38`,ZQ`ZYS8R``%*MB8J6XR'^RC@
+M!)$YU<S=@L!3JTW-9NZM<,N7#>!B!0NQ5,/$2#@@G:[D`37*U%ZD1(E:V$?7
+MKM#4R*"0-9;@QW?=2Z]74I3W-MS70#E(R%D\;P&*ZM!V5@^.3$=\4V&"!^2X
+M8.38RB"Q0`PPE;8H<_!5O6_$1@P-IB(\H.![0$%Z>+VQ0:_;:)'H,_T)RB9`
+MYUMCFHJLU?([]J1H"*WQ%,96:7G(RC8J!-MP!BAS`5-L$92$X*-M"7XC5UL6
+ML)5`*"_2X(W[AJX32$C<0&$^*H7>X,7U\))`UAVS1.X!0=CD_H""P0\XC5Q%
+M[!0O'J&Y.'>H.4+Z=A0#)V@6]Y/H%2<-P<Q-]"2O#8GFN!5*:<0:A7:-YBXL
+MOKPAI490;;&'`@(K+H(^";@C*)EVC6)GC:+ZF2%T(UJNONF($S>K.*/PD\<5
+M-YRN>X\*/0TPK;2.&VG=DK,5HRS?@Y.#B!DAH]!L2VZQ)7U*PCPAU>:$',^:
+MV1*"JW'J(4_E=J.`\M5!!18-D*?N(8M:6Q'Z`=FC#TCAU+6]KR(5OC6Z$CSG
+MABNF#8>.&[:PK0%HH<X+F1`TJB-?"]P1.F,&N#F4J>CU;7`FM<(^C8U:C&H7
+MMYQC@7#;6HUJ=V]#M%$D+2>=G7"XXP%RJ(X`IM@1+7)?TM3R-U>4;\`L[]VJ
+MP123TF](':XGY\F?JC67N?&;&INMQ9U:3S]JW")N-`.0&;;8K$UOKP;H")3%
+M:@;(*'M`7J/<>]2SSY-KP4RM,LW7+;76!H27;C$4M-JH600GYF+MD\C/+3(1
+M:DB]4$.<-E"J.@G%/5U[`E6W5'QA=`&7WC:XU!O;II*XSE%>Y/KU<,:@]N(5
+M<=Z`ZEOK).):,`^$1LA2:YLH6^9HTTBDC0^48K.X2;5-[&J;6%]$9W3WJ^MZ
+M\)&.!_&Q[Y%8^'#`:8C[&BKL-"33T\?Y7K'8ZF.K4(VB\N)&VT13[P$HRA`T
+MHH3K$S)3'ZK8;7X#][UAGJG5J"7@>M&9:7DE`'`#]=.HA1T=(U/3F"]GO#8$
+MFL249N'!QSY.#/C<H<F&Z[FZ;W&G6RV#(O2.F7KKS]9[H6Z$'?F45BE$0X%@
+M+THVPF1K>8RV18?O?B0>FWKB8`5_WK!44GS#E&YI/F)FL0F]J>`TZH[YC.>:
+MNOC('>]OI8U&2AN-U#%FC.[;>J5^-6$C(FR51CK@\..IVET-,$L5SQ'!GXY.
+M7>%49[9CTAL/L]'KCE1MUB(IMEF'C#8'DPH*/'5#0HWRE&L>#[@*Z@$WG#3+
+MJ4&ZQL(UIB"Z$?#XEJLW@:+D>D(RJ3%I0!\MXB+V9[8&[)BQ(H;QM/564M=;
+MR?<TMH+(5P2'Q(/[`Q)^!9RZYWE+0QY;M['5('0=Z[GL$P^OOWU!K%D"%S9+
+MIXK$0)&DKB(1'T*MT0FH*H!+`$[=YVAA%*6-X](3&IJQXI7<(@"+\!&*:"@;
+MKX7F:%>52J^?JC@BT[R%R<>7)<AV4MJX+!T3PB"%1ETBKK/]@'3G)LRMI%5K
+M"K5$^)J'6H0)Y"0K[G$864R%M/%5TD9%)*B(KH807M)$`[YQ@U)>-N27-/;-
+M<%G8*[9V15"Q_M20X$89--:YD:0BL_&"#%(J^,H<`52H8JHVV"3.?E]'C@,V
+M82.ZB4W^RZ@\Q-O`P]UWWWSSJN$COF*W`1D>BZJ'!/50[H`><`,-*^0-:PNM
+MAXQ;IA@[;L2FW0@A7Y$MZ\4-`J%4&_/39M;1\)\0M!T;)X8K63_`OKL_Z%W0
+MI7G`NU[#7W_X_J?[;QB!5>X62K`W`,H4<GY/O>V'F[58X2:$GC?,'4"2K?'A
+M@3;DAYG:AD3:NXSMH`(Z_&_'?,FQLF=03\$V\Y4+LTUM_B%%'F-.[7PC4A24
+M+)CJ^:1K0P2F60<^IOSUB]=O7KRXYX6HC3GD=MP+=4*$-U%?+CK]`+?S'H<!
+M&\5J-L(Y06&YV.BV/!W7$%@3<.)4>^"GE9V-3+%POPN)"7E1Y_8=<*OM`ZXZ
+MZJRQA_G.9)!4L,4VH\_H=F01T[.EEN0#:P^XB[2=9M!IUJ@-6R+GE>+C;IA=
+M1Z%."#Y:[/;X+5A;;C3*A&YLB:%68784DG8E;FR-FPR-[,?:3`(2%QDWJQ#;
+M#=^R&T"<NW$])6?4Z[4;PNYLNE-J/3Z*FX_,_NSBA9)J(_OYF%]/"=?(YXPV
+M/>K7.D==C*C0;K+'3+P(+>5;V]UD39L0D<[)M(APL6LQ;N8TMDK9;754^Y%1
+MV\CY[NJ*VM8*&'>?7O_^2/?]U=I[:G>9["8UPDQZC7K19@L6[\K8L4H&9229
+MKKIP2H8=7N4#>.U8NJR-36FF'I?QP;FV'Y4"MDM"9FSGCKCF=#%W05;ZF%KR
+M:-/9IO[>?E:\L=M$]607Q5F7=3%L-AAV[:3;S4CD3MEQ1X>T2I>+/'P%RZ?H
+M-;8,T.206DTMXI-0O0E8$)_9\*/9R.KNLFU8(;MMO4\T,]=OY,!FY3>0U6C2
+MD@?I[N9X^='G7PJ[GNOYO[,WYP@(##`&!UBG`QR[`?:*:H4!V6=P$.%FN:I4
+M$N<NJTI2\W*`#Z1%8[XZ@`]59``V=)EP\%TALEGW#LC4AY&`(@:V$(V#,)`6
+MQJY*E5`,Y>SG<U@'@RHF30]4Q3$(MPY0WAJS1`=5>:$#/!6\-0!"KK,AUY5?
+MI@Q8U#Z*:A<'"!KU:33O5LW9`4*R*HBB90UR498)55,2"I10W&.`H37`H1J@
+MF]0D'&#393-4U8F*-5[#.5>#26.NE`YDA)"5J_+[`%-B@*.@!RA4A2BK*AF'
+MHJ:(6I]Z=I,&9*>/OF!YP1^3-06(DHX?LM`&J4]%D058]+F2$+(H<NTSG-U0
+M1V`9Y8"H]3?PK0?X)FIA`W=Y8<:JH!<;&"A=-.=J[<"&5HK02FRH$V%S:8I[
+MZ%HI1J/\&B=-1%A@>3A'N:Y[Y$.=W9\CE69Y?<#MY2^0FZ3?T$5Q7-KKM>;"
+MZ%M#98`IL4*?A>.+`_)3]*&GNLN4'L+/*-E(AHX`+6*\B*YI3_#-`1MQKP'H
+MV:!SXI^T`_;B@-'TO<L_EUF?%,;EJ6Z:OL!4Y6$"/*D``SB)@@!IR#!%F0WN
+M37^#6>49FBPB>(X)0\=B%6+&6M(930`C%4*`OR59=7\H2[Y,&&`"="R5#F7E
+MM)0%N(";M!Y+RD7S"@CX/C12,E\#IA>UM%R+3J/+%X#D_M!(KX`PGT,C*Y\M
+M<D*[UL%D_$4G?0C^>0KVO`)3?PA`;J`4KB`JKP]@^-_BQ]G#;W5+ZK?GBR%R
+M@*0]J_Y.S=]3\_=<_QW'YF_3_&V;OUWSMV_^#LW?#7RQ@2\V\,4&OM3`EQKX
+M4@-?:N!+#7RI@2_%S=^_7?[XC;8^IS_/'FX.6BU0#NMIE8E!A]+&_W8HBI^-
+MHH38D%$X<^,_ZDU;@YQ6U`S^'&$LFE]IA&[(-2$I0CDHELKF7QQD-P'E!W(A
+M/L5RU?Y&0R$/R$=".9`L_NLOGAUR%4V/$H<6]3QYI9LOKC6=H:S@Q^I-J:7]
+MAFM/:KW-(N5_4(K;?'-]D)SN(5?U"V4=OK#SS>=%'5+<3L+[%X/2_LY73P\(
+MI*IB?9"L\$%Y:.\[Y`65)>NBUJ$C5MS][""!L2'7Q\K*E#BZ]QDEPPPXO*3E
+M-U4R]+Y#M3:M#QL+2RK,_?&\5D2S`8=D!A54CWVF[":'JJ.R74B/SB_E*'%5
+M-I%EYR-#>B'E9/1L(PRR161_>'WU],.KAT=&CE7UQ*EP%98G;_O>*\MYEZMD
+M'J",WOXY^T5:MXZ">8/N!+W]>RVJ&W)MR0-4X-L^YZVWHL!B*CTW^R[8$P\K
+MP'1D!E3#XFT]!`AH+Q3`MQ#I_L-;(1AQIZ<3/X?VG`?=1'A;!U$Y*R#/*KN*
+MT]O'!X?,N1"RPN_2.RR@\_F,CB;39U_U[?!'HX<]<*`I>W!O^UP=-BL_4"^!
+M':1W0']$$4(SYHI_.2#_=@"T)NTD6B4#\`X4*)=N<16"LGP@>_]OQW\2!A)_
+MVQ<<X,9WH%\M0:DU1:W.W\[OQ$)R:Y;5TV'X?'H']!E9]C1IR81!H^UOIQZU
+M9>3SY(OPQCN,;JJRTY0\-&@(_JWRQ^F%7*A$&G-XY=W(KRX<[4OLOYWZ4&/4
+M*O5D_6C?2?X9+=$*^1?S`IAWF8&5PN8V)T+B^_$=%C#G=AD]*H]8R?P."(@8
+MU]BZTC#%HMY!@=E\TF^0<Z7X/KT+_R:G9]-R85PE@?-AN+S_9L#VZ2+<5O?U
+M;J!-AF$-6Y[AK#!J&YGCP_DFM*FE<0\:UA#8D)B<A<[06'NSV@1K()6"LHMQ
+M<J5"XOR[,ZTIKR&'/3#YB/F@Z3F[<-HRF!=*,#D'+,O6%DZJT'I=PLEUJ54:
+MGK\^PT7W5QH0V`5TG<'3@Z;K[.+3%X[,*CT+0$.IQ/P&T$F)7`"U!=\O"W!.
+M_8^*"`J+[*][T!#WFKZS!V<H*=*58'+IDB+<N0'35%#&PA-9T'_^ZDPJS`QZ
+M2?0N,@T*%TO*SAZ4OK`;5J`*9%JM",UDOO%%_(8ZRR`G4V>VBRF<U*'.E+DH
+M[L,92_EB*CA+VW?=$FC@S)H5<-(62D;_^8LS.14$..T^G!Y4+=DY>W!.A4.V
+M6B893E=::-.6B6)9QN=XT)1C78#S'\_DR-F@X;<.G-)&$G=VN:@4V'/#[<87
+MENP.$]7DR6E?>0'`106@8X?;%[K^]*!I._OTF0/Z4\5$Y.1_H1;[%DPW5V!2
+MSFS&__G79UIN_H!\]#TH>=]`ML#6W)]=X4DEKS_';F<!)U\RJH[)9ME'O0)!
+MX2S]"").2^7LGQZ0?=[AHJ"KGDP/F]G.B@TVUQO8U`/;`;-A=EMTM:ZOB/BU
+MZ.P!6>A[<'HPGZ07[3-1%IVA1J9-A9^Y@9)2DVMDIK'`_OF;,YSLQJ+'+@\E
+M!=-UP2RV&RO)F?6E]WO(K)=\@J!FY.N:F^-G!V2<[T+)5VXH,K=`/B`A%!15
+MPCD5/IO?JB&3K6)51"86Z!?!B0KNE'#>0:9)!6GN@AD+G6@;>13F(C2RM3X:
+M*ZFT#U;5>'^F5_!H9OH>F)24/2B?[R/3%G:V:3G(%1&@K1J*H0$SFX6KF<%:
+MSILB87W?]/#9])B[2Q[+G4O\QQM_%=1EV&JKE-+&`O7EQF-DMJ>TL4'SVCN4
+M6EA,TS[87KV@N2:!4`;'=C52*YNR!EM-XF_/Y"6`'+MT&@L%W\.MUQQSURI.
+M)=0P[K+]U`+J"^P3/_&I#VS0SQU*%5IB@ZE'J4;AW(BG'%8U.Y1:0\F%TC+Z
+MB5)YIUJ2$&W/.XIC87_N@SFJR3Y?M%Z'(B?8/7[RC?FY%JA7[)__3132?/SX
+M@%SW?8649>@:L=\73DZ!K/1FB9K@]HR0C00M,`\'SJ@-8F,'1MD=8+]H%\8)
+M\UA@3"TB,Y!^Q\T,XT:W3P7JB<F-ORCS1CKK'0HOL[O>2I93`Z;+6G]''7FX
+M"84A[U*!_7-6R@4V?0=.:PJOO2.+,IASXVX4R(F[EM*&R[-]O.#__/LSLGWM
+M\:,#,M\[7)[-^+&[Z),691P;)S-[C2'MH=.UV"S$PK+"?SF3XI7`INV`&=3+
+M#'-WU46WKG"V[EL.'89=ORB7SB!`RS2O=8MF.$-=)`#:]8N<(C1T=!!7ULOU
+M36MSR1>;6#L+'UNYF2V"=1?J[@S'>P#HV(LNJ)\9>FHH*)W/37@A%CL1<=P1
+MG,FU_MNB)#_.VVQ?G96W`7**_RY"U?'C(R-=\S,OO:\]XHR?:/;"(&%C?-AR
+M&_'^#`>4`>C4M>95$X6.)K)T#=,S`;1513DX&'>\HU5[?]Y(T&JC])?$\HLZ
+M/^JAA(YSE.'LZ")#%VEH'=9JX3.!QJTRLFI<%H*I7``),`#K9.UW/&*CJ;'!
+M]2WE3*"I8:1BBWP#)EVS]<=ZW;/;%ZV:R@IFZ"#39NJT/<7N,Y!3#60.%\2P
+M$_URKC4_/+MKO('_ES-4%1KTI$:'-KUJH]#51D9UNYD;7+HBLV$+9J.+7)G5
+MX-GD3(60-_NZR&ITF#,6NB:GFDEV;"*S66BO<%;_L5T5]W24S:6M4-NNR,GX
+MZ@QGX@<]@-*)T^<#&7-7E6;@31UU4.3O4D*(C3>?MZ)7Z_R',Q2ZR<=;]L-A
+M=BQNN.H`:;*+U&JG;*+NQAS2)@Q:B(L$[G=9ZH\]J9\)UJ<>P3KE*^L:?>\5
+M3K>WY$C5R8QEUQ2X6$9MQ^,RU"<XDIR?DJGJLVB8>]ZH5TO`]Q37RN'_)E-H
+MMQE"*HX9[\QAWD2<0[D?(@ZI"MJN_^2*PU<=@L@"S(::(%)&M-D#TC0.5"CT
+ML9,PE-P%)V<8.P9@#M][WQ4--G-7;`E"I[!CKT1-*BYD6"KW0U9U,!<R+'87
+M7468=WWW)#-8:@W563W5><>N"JF1`SD):8V,_7BFEX[B>%8'G:DXIM8#<U(K
+MU39ZR^J5?7OFM-4@2[8"\I;%&FC\Z0REMA&^[XBKQ;7^7$_&]<15!K/16\I!
+M82]89ESC-V<+8`W9WIW)<5)$=+H[2\4YO)[9MQ#M+R6>,S:&=$;E[@;8O`GG
+MS.5&R-T9+M04>[]'F#DD[CK:B4_'H"2K:?U[M;##GH*RF_B8*Y.-$(9(V?#K
+MFBHJ-EU70WE=66=K=-H,IM]U2,>-AK+E7@C%(4SIX!\?-H!R_?!\HV'/*W'*
+MS*Y64.LB?GG02XFW8(8-.N-8;H4,9RC?K-2Y!Z;1E#$^4MD3G%G7.-]N?&8F
+MVE5#QK0.5/9C5IY=(Q&NP*??!306^YZN%\<S!1&Z=M-&H32[`JFQ44V=#<BF
+M="@"^&$?GV.QW^U\5W(&U3:N<9^BBO[]\&VPC8"OLA[/92/0V.+0[CZ<(1-H
+MWWW*BLC5BBC[S?->.)QN1/Y];9#Z<D-$K(^8LE[?9Z.43_KU%-%*4%<"Y51C
+M,\.XKX8V08CY>)FW0[X[DTJN:B+MXM+":N=3N;TUSY+'S74((G/7SJ:--6WR
+M@*^+T+!2S\FC='Y\G]<+.,=NE#D#,S8B/JJXVMD#\^UF-P>7'O(6"/;D0V'=
+M[P(:"NO>SCU`8]Y:,(V,5Z/3[\9PW48DY3-CJ]=S>R:WB!2U`'9E4LA@=O>3
+M4L:GK;>]"@[:"4'D;;%"M9=;4!QZLD6.@]G71:;TZFQ'&]D*'E<+SU"_V4#J
+MVV2A(CHZ2K#99E?#N`X^D\I.^T@HK\):*3IK3&]WOAI\ZFWHO!'RYDP*8^9@
+M24<D:4C']G>4YHH$\ZJ/&DJ>]W(QO";!9RA'#8Q:3KRRY7:-Z6HBHX:2]3U#
+M*4P52U<[(7,E"#9;7XT>*G8D9%O>F$(/F:XBRHK=]K>5?"4A2_LC[]?N)`K%
+MUB%B=?>0-T*^.9,R)T,NU;(#IX<!RP4#.B+)SI6^J?Q+-4!V$J_61")SO*ED
+M?+D/=?[7,RG7EX/,'1&O$MZ:OC57J^[2[+0<'9[WLMBF-GPSE;LU$;D89B[+
+M\NSB,D?";4</I6)K(8R-<YDJXVFCU5V;V1)P-&PL$KS7X`V*/LOYI+G8%-E%
+M;]1;6FB?O2>A@JF,S<I]=Y6!NH5];B1I(0Y<L;FT+YFLLI*9^JL_5P9[H9#"
+M6%GY&U:J0,L9=SDBRE6L=D!33X0+6?0TN@)6YZI:E2/S;NZO;6-=0<,LI6=A
+M]]5.*JP-$WMJ/.,FU!Y0Q$8$G,VMWMFDK8FFFHK]+>M[')T%CPE]DDN5]UVR
+M=#Q^4KCL&\&SL/QE;;4YO8''IV*[?0^\N0C)FW[BPE2%,"J3,B_LKKN3TB90
+MI-@LMH5C1]#D6('I.3ETUJR(`E4J,%:1HXW=TR0M1-TZ,T42E4T=:\=FX+H:
+MQ=6!M&)=%_GQ61%\V[#$M(ECZ)-LX]BI`YO/[&KZX0M7Q2*K9'U313`;Z#)7
+M9O`FO7LS)QYUI%PLBLYT#(:IBN96\8I8Q8!;%;<)IT'LYC1.+DJZ3V]SKG[3
+MW9^J(N*E92C*!6'TC=^R#:%-^>K-G/S6DR-*;E/7:LW1A]CL[.3<";N[7[J>
+M^*R5[HPBUF4ZX:YR<"LQ7^.&[]YF3K5C4T9VM/*.VPOBQG8G9^1@P5@X>,YU
+M`3/'(VH3]8).Q49,F^26X^#6[C%INQL^:IS4%CN*SO<"#WG7HY=14-A1<7.B
+MQE>;C0UTH3U<$96K0V'8N]#AA1Q@ZF_(F,RGL47=6&WCUL"%UJZ7DMU[B[AF
+M.N^//Y,B+D:5G>V-%M(A4F\(VQ]B:H>8'AUBZ@UA^D/,[1#IT2'F_BSV#VK,
+M)/OJ(>)C0_BQOQ;=(4P[1'AT"-,;PC\Z1+'A/^V,Z1\=T_;&[!QMDB&*,=/.
+MF.[1,5UOS,XQI76(S6K91X?PO2%2=X@MS9E'APB](:;^$!O.&1\=HLO_<W^(
+MEO\?97_?97\S]H>(S1"/LK_OL[_I#Q&:(1YE?]]E?V/[0_AFB#[[KUKOAT(T
+M_R__K61P3\?W_HM[=KR^T0NI>H"X/B"N`:0O)*SE(3V&#%3B=T^3SASMVQLR
+M-J:0G+3I#SFOL^0!^>:KO0&Y6F?GK&YM&*YEZ-XZH.<!'=5FWALP<01N_S!K
+M8R33@>RWS?#944K\!-\+EIC4/?C7.`SAPKQ]BC*BH?I2^_L&IB=-;.L_^?)0
+M"H2S>PL8<9WX1]>?73V]D2(Z7*=\W^`V/;EC6D_3[@%CW@$87P)##NPN+(XC
+M6GNPC%N?_!T6__./]3*_?</4=J15&Y^8WX6V:;1$E?X[VQO=0WQUL.9MK!N(
+M=3^[_/WUTTLID\17&>U'KFU'3*4F@)7>(J0",7`>ELYTL+=B_0_\USK*T/]O
+MX8UEA,\^NWXV?/[QL,B[X9.GGW_YZ=5'O[_J?[3"JS=I=J;HNU.T]10?40CN
+M[?#S#/[(X']V^?27-\/'5Y=?W#SZ"2L;]X,.0=,)7%Y@+UAE0^]D=VBV$-.C
+MLV$1_PQ5C+IABLZA_#J@'1ZQ"#"8EYIBT7="F#9US];/M;L\O\/,;K@N0>@'
+MKG8'<\VVAWO,8LM3NZ%%LW.O)D2]Y6,?LS1U"I=/L5'>$WLKO2@,U-QQ\*37
+M7+HG(^;RBZM+PI&S[_:%ER\V/7UYO/[PN//"[[V@J7UYM?/4;YY2[Q]>?OKI
+M7N?T//\GDF;]=\7*'PY<0'I9DEOD:_^T\L],EP&XU5CB'];+]?"+-GK%_>,*
+M$J_W[VEERD5>OI23QJ?BY@"N7_4`1^6$5$VZ\GLA[#N2_EQLW^3KR>7B)LID
+M.;$@]<<[K?2/W*L3+J9Z*3=&C^ND+*YT7IPEFF18R?TD1QLTD?PGGE,2Z*Q<
+M_D'1B9^*BPU691U6#/!1D),4Q[@M[^H*%TEN8);JV[QV>I[S):43,D96?/(=
+M[Q/UNH9%7\I9OI-4NCEIXN2)JNH!$,&N0P5_S\NFMYCK/99)=RZL+*2ES,P3
+M=C5><9`[\#V-O.AZ%3G5M+A%PB#K$<=E!'GMDGPQ"4:XKMTMS8HF15D,KQ@C
+M"?>/Y[M8PSICN6\!9M]KGN%$ZTQ+)41AU^7@@YP_'9!N<XL#KR?^?EKKH7*B
+MFUXQ^U*2WM_01A"7"E]6Y<VAN$PI275S!^R8U1:YA4<AV`[Y,M8[*;URTLRE
+M$XJ9,$,)X$3$KYDL9]!16F%:W25/<\JWD-".QZOR5DH*;_XL%3].3%&XZE/N
+MYG/YJA&Y%GOMVO&T&/VT;W5+($_R\=+_'6S8GY$$Q^2PM/F92U5:`(,KNOAV
+MCK!B_)X7BJB6<Y-^+F]JH=2)6RWY\AJY)8Q*G@R?USX5]Z9;6O%;;.;=ZAV$
+M;`C,PG>0)1[B*GC!ML$:);G9FP>8]4X4XDB2._ROP<WULUQ\,HF86%TD%GA\
+MGP4?ZOCI@`UH)DL;92(B6SS(960(9ESD,^86"R?^K.F[MT2=5E;2$/EP>M^=
+M5`N]1:[&2>H7/.@MR@=D$3V4MRN2)W1'RAER(8":A%%GN<>#C/63;J,^2/&+
+M4WD/J0.C.=$.>ML-;AZ<44(FR>T)#I.E+(/7+&2`9:[GGP"VP^UN$9=8S/D2
+M-2$8BG>^DC(G+,46#K^CIB,4#*F>I3N]ODDN$)EED=(ZUULR$F4:*HGB*%0<
+M2+_POM_/ZH4QVT8H(;GHQ:S<P/UZXA,C!)JO0*'S."\/<H)6`"?NE[M69J&D
+M&1>.X?9WV1-`4OEM>7D/KERA^,K/I!EFJ@,]BTB08_-,44'X7/9\D][L8`FE
+M?(+NA)1GZBZ!JT#*WHOFFW&5W3+B'<OU&4(H02P%H-VP1INQAA"`"93K82[(
+MTGDH`DM"4R^BLD;T/&UQ_,3R.4*A\#TIEJH@WS$IXW)L-T-#X\8M9BC5@E[O
+MKXSX"I=61[21NVE(3]V3CBCNH[RK+I:B*E%W.%,F^,"EQU8X@(65H7UIOA%=
+M5H#*XYP.2+K6T\3,\'.^EOR$79,''/9YPQNK<@L;*>N5"[&R_(.S[DZH8RD+
+MZ(1X(2E%EQD`,\NRT*FP.R28W)<7K5H(.Q^@,;`F2R>B3&@IK!'='//5D%;M
+M`#(Q9]R*1OD,),>$BV9H>BO(2&)-6@-)84&T!F@&BUG:JWNI1UY.4@KD-5)6
+M7B*Q\*1Y+BAT^!KY][=ETFMD:X(+K8D`()*WT"T)HC/*_341RTE!-;Y"5G57
+M)DDPKW,01U[DK%"B@Q@T5GJQO"0.`Y!C_XJY*HJ&#V(4Q"AFG:$F:RF<UT(P
+M(RZ\@FWA27[@NF]KY<JN5?4YXL2$&X<H$_0-U]U$$Q@0,J.4S2OQ*SPT!04,
+M3QQF$%8*)&-B5M2>E2$1XLOR_K`$P6=%R'"NZTL]RO.*6!ZN@EZ+RIIGEG_#
+MBM9[*5=S0E#MC@@O0=$E^":PE=D&F>7R0LZ_/<EQUY>2DW;2&VD?)%'^)Y(K
+M4.YV$L*'`K=@=A&F2>N@C.*SD>2]8X8"R3IPHYJCANR_`'>)$@-AAN-^TM7_
+M*B^O!5/C4OE9J,TX`<M2BHY`(T,Y<0F66<Y8X8!;RT&1#@3.=S=-4-54:>:N
+M*/8>A$XLY<8R"[#&(8/NEK%IP*E&-%LDZ1/Y(BB5Y["3B<EO4;CGE:8%H)SL
+MF[QY@M3#DY0$Y`6.JQP@@7E+0HDM&X?^R7!F@1DAB8W8RT)0Y$2+67%\[Y]^
+0]]ZO?G=^^'_[0&'EA?,``'E+
+`
+end
diff --git a/usr.sbin/pcvt/demo/xmas.vt.gz.uu b/usr.sbin/pcvt/demo/xmas.vt.gz.uu
new file mode 100644
index 0000000..ca2ba3a
--- /dev/null
+++ b/usr.sbin/pcvt/demo/xmas.vt.gz.uu
@@ -0,0 +1,110 @@
+begin 644 xmas.vt.gz
+M'XL(".RV^BX"`WAM87,N=G0`[5U;;R.YL7X<0`A:/T`O#/9EL]A8S6NWH#B7
+M'2!HY"#8Q_-@XV`-'"N>&6EDR7/Q`H/][>&UR>IFR3.>5LXFA[W><9MB?:PB
+M/Q:+U90T^\WB#XNK;G'UM\6W?UG\KEY<_4G<Z7_D=G&UF\T65\V:L>Y_%E?M
+MFM%NN;B2N_WBJMY=+ZY6:U9W2T*(OJ?UFJXTBM@MS:NZ8/?++^9.OVQ*KWTI
+M7=-6R^B&*@.UL<7?_C`GJ83!8VO:6'`+T+]VK36B?$U5;,M6T6AT[?'FJ<C:
+MMVWDQ)I*8T*B6[BN*Z_H]G#X\^'P1E>2:\Z[=X?#V\/AO?Y3F3^-U.YP^'`X
+MO)[;*E18)7.8UU:(\JXZN.OC86/_-__IUQK3?8^ZXJ/^H^V[3\,?7H?NTIVN
+M.JV256AE_K`OSVSGT^[/KJ-67?63[18FNMK:R9INOJ^<AK(SVFT\HEPSWGTW
+MGUD%:%=]NKP<_CC=9)=YZ7-^%E=,D^&YTMC/W'62[DU-2=W+C[;3&U-BARAT
+MMG]%L\!UDA70@V-[2C/Y%\>?UM[4IL=,Y0O7,\*6"M.U04B7MMU%P"*NF^ON
+MPHY'8\;,4"R07A>UH:BZ<86Z-W2]C6&YM<$*T;58:S.B6!/%$KFVVWR*@BT4
+MG`6;2-`^F*JM=Z@J04U@FVYSF>`V$)=46R<M4^E$7&GQR\0@-9)W=&W$"(`X
+M!*GMNMSX0;7M#.3)+(P3L5W/0H]S#%(8FS:7'E)D(!T`PP"XM2H`<!2`8@`,
+M&,40HWK.$3=\JJM^\:-58\@4V$8QU=0*`ZB!;34*T"(`&CFQ3:T0VRP)W1RA
+MEE2[*I)1-1AXFYJG6E0[A0$TJ7FJ00$D!J"`>0HQCQJOZ'BH,&KK-E)C)*H+
+M1F0E@#$HD15&9(V<&L,18ZR7<YY#CY6W"N.V;BRUBJ%*8116%%B%4EAB%-;(
+MJ54USD#K,DR@L1KY0]EV+_+PNMW$0+E"]<-(+-O40(F26&(DULB)@;(YY3Z"
+MQY`2M4<!>Q2J#L9C*8$]*(\EQF.-G-HC4!^O5U/B@K.5IZ'$J"TYL`KUT1+C
+ML63`*I3'$N.Q1DZMHHA5UJ^3L(0YJP1&;5D#JU#O+##O+%:I50(EK\#(*]K4
+M*M&>\G]NK/2"/)Q<`F.V:%+[!.J>!>:>A0+VH606&)D%B#?$B7BC\?&&CI&3
+MD$]#8S07(.X0J+L6&*<%B#L$RFF!<5J`N$,PU#,ZX\PHTD!)C.8"1!P"==<<
+MX[0`$8=`.<TQ3G,0<?#5*7?HX]\827&,Z!R$&ASUTASC,@>A!D>YS#$N<Q!J
+M<(6O8\Z!U'8+$^S"",Y!U,%1;\TQ&G,0=7"4QARC,0=1!\>C#NWE_7A9IIJM
+ME]\#<8SA'$0?'/7:'*,S!]$'1^G,,#IS$'WP^HGX-]EWSGOS&$9U!F(/-J!Z
+M]:;7#F,U`[$'&["Z.O0`&*L9B#U8DP%(]V/&SK!6LS'17SM,$($P-<8,6HU)
+M?7`((`1A`U)7V^V;D!;A&`*(01ADM:Y?V3R+QIGY328)@;$WCF'`(!!A?`3\
+MV@,[F#&QW4Z:@7"$L1',P<&X_3C#Z,U`4,(H!M,GXGS\'P-)BA&?@=B$U:>@
+M7_=4IQC5*8A4Z&H$MXMPAPB'$9^"N(6V([CE`&X6R#M(Y%!L9E`0N]#AS"`C
+M53&_3T$,0^%TT+\J$E7=13C,X5,0T5`YA"-5A%LF0]XGB^HX]MB:0$%H0\5(
+MY4?71LQB46QUH"#(H1RW/DV*46Q!H"#DH0RW'J;*W(8W67(H-J4H"(`HS8U[
+MLWOL@;#Y0T$<1.L\3C09FS@@&EKE\FV)I318*D)TM%IC4P@$1.U8/;^OQQ)+
+M(*^$2:-I%["EQR,\'O,4-J>4+*KH]A5LJ3#%T*`>!*^8-!H*@5CAJ?2+,"M;
+M/Q<Q-PR\,*90D!YR$%#P<X13V62>=6F@4W<C.2`6&ZS&34#E^HI]/1)>K#Y=
+MAK8)F3BC?S)YKJRV\<&5LMUN=+_NGZ3042+?/E+9I/*Z6Z[GB03)BGP:RD`A
+MUS%1;!Z?WFSFGVN)906$W6*XAC!!EN2%R4@Z&G,9[&%Y:89)LZZZW`2C1NE_
+MM>9Y/([A<8/7:R/RT@*3%L`6F9>6F+1,;<&R_FJM\K`*@U7`I"8OW6#2#3"I
+M3:3]RADQ6@RC30T[D?)7ZU7$7UR]U#\1?>70W4.\E;/)53"DJWO!N1.T/X%[
+M=>>6O*2T,:65=SK*+-@>(!%/6J>6O^$%(TV-"KXQD.M7)KX@*8R3D9ER`\2Z
+MR[1$N6@G(STL=@\S!V7*Q5VQK.KCS?X!]!AF(.)&:ISP5RYP!,@IM,Q`IP*]
+MCU#=`.)E@J(R*+V`?;#K#37T`1`I2I-!<0*;3\FPG<K_*^-/S(;%=N#+41-M
+MIHDVH46OZ,JB`%X9^14B_VD^'M0\A-U?#2`&98-G`,JM;`-NC<N:3)ER.TZ$
+M6.EK/0;+8/`!QB;!R'"\+ZN"EQ\^`%#.5V)Z90COROI5E,D3*F5([<IZ.IMX
+M[#":LE")#*F-3[?K9OX!@'(5+)`[`1+6/-5=QO66-9ZBIHXY0J)=KUOC[.0-
+M,DV7KK)M!$["$YZ--G3EZ+U/9?25V2<'76X@;G;M7J5V\#KH=#.0=9O2=\ZT
+M9*FN4Y-T/=?TS5`\;KD'\K0#H4,^DZ],^K`B&62&(S-@F(XI=AEYWGFSPK;[
+M[0B'`PM%WD+1]5S#]!%=NOX.T_K*Y'^ST/)I:`E,U83]D,%QAZ_>1ISW(QP%
+M3&WR^C2I/KGHK>D&$=0@TZ],"C\+W3X%W0)#-=%?9U#<D:SW.,HJ-5/4:3`6
+MP\NZ(_GPL@;C.$[X*_/D)0M),4B:FB587IIAT@R8P_/2')/FB3EXGE^9AV19
+M8($!"V"4S$M+3%H"HU1>6F'2JAO'NIGTOC+/-;/(#8;<`*O:O'2+2;?`JE5>
+M>H5)K[KQ-BMF]95Y")T#E!B599T:(_.LE1AK)4V-D7G62HRUDD5CQDE\98X)
+M9/$P'DL.;,F356)DU2^DMN3)*C&R2@GF4!+FR3QQ)49<":(*F2>GQ,@I07PA
+M\^24&#EE"R?-(-VNS/F:+"#&5PE""Y4GI\+(J4!DH?+D5!@Y%85#,LJ@*W,>
+M*@N),5:!@$+E^:DP?BH01J@\/Q7&3Y4&#^.4N#)'UK*`&&45"!E4GJ8*HZD"
+M@8+*TU1A-%4-C&4'66]E#A-F`3'F*A`6J#Q-%493!<*!)D_3!J-I4\-H!\UZ
+M*W,$-`N-<;@!84&3IVN#T;4!84&3IVN#T;6!8<$XZZW,:=TL),;A!@0$C819
+M+"_<\[4Q]\8`7UVE2:E06[GHWU2V*_X<9+F5.2,=I**0H:63,3ST\&W,/(5Z
+M;GEMS(W/3S4A./(WC;FI/I4SZ^7,>CFS7LZLES/KY<QZ.;->SJR7,^OES'HY
+MLU[.K/];G%F?<N-BHW:^UD.@`_8K=U3V;F:N/WSWQ]GL]'O6PV7?%[X]A.OC
+MQX_]_1LRN!Y.71;IT5B_^_OM\?@S>7EWU(::XW_4_+-SK^UN'LAW1->PKPU;
+MN";XM?0M..A7#^^J!-IAZI>J$2:Y/R*(Q_M$:8>IU<LH#0W*(.T/QX=A9QT/
+M>P_O<+6*J<:)&8AZ^_W]O7E#O!Z1^_N]`WO7C\[K`+7KB]Z34]<C>7001K"7
+M.?P4;S789T)$C@0MME^HQ3/&,0_SU6.7`WWFB.5T@X2'ND&]44!-I<-KHL?L
+MZ&99'+T/'SY$0N1$39:%'`_W^Y17]X>C?L'D)]:T/FI/8E,L6_/Q%?JW+K>Y
+MET?SL1;FMZO0=MN#K=%VYI&A3>(\$EO'W+A*C:[TT=9J=*T/MEJC7W58]LY5
+M-!_8\-'AF8]K^.`0E:G@,-VMJRQC91DKRUA9)I5%K"QB91$KBZ0RCY5YK,QC
+M99Y49K$RBY59K,R2RC16IK$RC95I4KF.E>M8N8Z5ZU#9[M&7ILS\M@N**[#[
+MZZ5?.EV1W_93D\0RJY"M0H(P,6UX$!TZA*V_K>Q`[&>0.!028"!*A%^ZX-BA
+M!`S:[_EW2Y+`DJ77>X`Z!/6QF[/&H#N-`JYF=E0V!0.&!U!35Z7U/#CH(M=(
+M8JWOA]";H:/)4-.T,WEH+BJ:Z$7ZL?(8RZ`8Z85[<X>*D.&HDMAUH6;C*YXV
+MMN_0H.9#:".Q:17T:U/&^"R#WJ6ER_](=W*"`<O``!([%.B_S!D@S,;NP=XH
+MT<4F29X=2]A[J[[WR'(X!DGS<1RMB<=X8819A4Y<Q4XD"!M'TS"T)+K>D8.N
+M3`9KU6'D3V?J<!+%CCOV+1P?^IF5)7WL0@+)!Z;K<,A\2U2W%,8H:>A<4]BV
+M>#S:#[<1)H.ES3N>;6X+DS&Y=]=7S_6@NM.<=ZGBYW$!KD4=3H;.TA'F<5K?
+MX)NI;5]H(NQT:SIZM9>^.X3B?Y'#J,,9IUVOA=,D%)_5B\QF#EZ3QOO),!NU
+M#GV8OW>%37=!YON*;&;G\32N%=5=;$RS03/CQ5USK`OJF4^(<K5BQ:G\46S!
+M7L%8T=H>,G[6N2A]Q?ZJN]'V:G8.%Q:FR(5IXL*U=!$T;+I4P43)(&5[;.,$
+M-U&_Z!2F=("VQ=`;&]-FU-/K>!\NY_-[[GGKQESSX=\4SC*T9?H$$$ZKYU5+
+M)N-]JAY)R'<1]7MXAI<-'GM#`-V866_[T*57RZF4[K^]6MS4OS#]Y90*L)&.
+M:3?"T9[,?P<]@BT;G*S,KO</?>22F)@:Z/>"P42NH4]2N85;A&>L$:$9S3W?
+M?H53F9F,L!NBX]&U9^(Q.&CALP^Y/<WC_U*Q.9.CMJ5SL`6N^E',3`<6[,D[
+MD2?6HJ3E?EA<^X$_&V1R,)/!]J-V`)>QVL8)W#X3"-9XP(O0"<CT8:D9SJBG
+M5C5=0,Q&U.>%_(F<P\=08/>DQ&[L5;QUZGE]Y@-S,[-0F^L-O0=7V#?W*O@M
+M=:^!WT'W"M"H@'GH8BP/XWW1<VPT7]G`.Y]:6/UHF_,_7BF?$P@Z^0Q`KQ(#
+M*OGIY!E.3DU=W2F^._;@\ED*W[Q/7_C6?:ZB;YPGC?MI;1?XZ+QR4YP%^_.+
+M$_%)%:>!3[8X!7QBI6]?)(1HK)5V=J7S/OJO[,S74;8S>Y#.LCD@XG(D_@"3
+M39W8+%#??K@U2(;*?KHD/N=YGH&Z!T$Z@CUU!?<H>L:YSA4=L$-9.WPZC-C$
+MF4U\]5:HQ`KS6:4D,<+=YQS-:3_CDW-D=_`).[(]^.2<33:I_LZU&AQ)<&&P
+M"[_,%_GT(=GYA*(Y&&;3A]Y>=^,Z[L(/3*!-11)-+K[$U?BT)O%I3N+3FL2G
+M.4EH<4-27]V3P;!R\R6>91[>C^3>1_4-)R_O;F^/#[\UG>_>=_6-Z,O\!P@'
+MMEP`KEZ$?MWX->YS/4D8O4T8N,J^N.E[:O,EOB%R(6EX$[G\C%D^[=2<>HJ<
+M@_K3TWI:VA82%A(6$A82%A(6$A82%A(6$A82%A(6$OY'DM!]+-E_+;Z1=D^>
+M_/'KIV>][@N7,$.XOYZ>OK&UI3L!&DZ"FM.:T_,[-O=(R+<FT_;PX\/B:O<[
+M,OSS\5S3`:C@KI_ZHWWGF2_)D%[KG_[)U-*<>IUZ/L'V!D=NEV=Q^6E[B7G:
+MP.EG'&C+GZ&U#XW./QVS[9UE+F*FG6$>XKUXIEF7;_!\,PYI[DS3#31VAKG6
+M+?[^E/.:;K*EC7VVLYYP\O7MXVO3=-.O;VR\[)+))Z!N[$PS+2)/.Z52W.GF
+M3HIZKA4IOP*>:TG*+W]E32IKTI/+Q*33ZBG'>9Y5Z93OG'!5.MMR4R90F4#D
+M\P@]]00J\^2I1?0_<*(LKH[_'BDZ8?#F+U[\[=7;:NH)X.#;;OZ/[6TUZ328
+MGO^3$G]29MM>U`7S%S_<;K</WU=G(//D7)V8JF=@9B'C\\FX&GB,B:EHW^&4
+M.HW"RV?R4IC/SLHYCO\/1"U.\M=%1CD.-::EHAP&&X67S^6E=!''S79;E:7\
+MJVDIC1-^=U=X.0$O5:W[\N/-S]\78I:E^_^2BLHM9S_>_;8P\6N9:+^48?[B
+MO^]NWA4?^?7$E(:8F_=O"S$G(*;=[+Z:GI?VJQ7FKQZJ<MIPDM2^_78''6;M
+MJW+\]:O3]O;;)>8OCJ_^]_;[0M")SA,53GX5)QN?/_CQ[?2,M%\Z-;^I"CFG
+M(F?C0KH7^[>WOZ\F)ZO]+JKYW?[X<%N5MQ,4BOX*_&?C4A,O]O=G8J3];JWY
+M[=NJL+.P\U>PNC<NX'[8WK[ZQ]U%865A97D;8.%@X6#A8.%@X6"&@P1\9&*9
+M<V7.E3E7.%@X6#A8.%@X6#A8.%@X6#A8./BOW8.!\7V2A&1(PLP0)R0D@(1)
+M7ZZ@+"$G*(-0,*)YRIQ$F\\&G^T-5`_OA#.?)W5E)?OO'W3?]!?.K^L*X>R?
+LK:O,4Y_?D[\>]SORP^WQS>WV]N?PK-K59<Q^3M6?Q'9Q/?LG-?=D49*R``"?
+`
+end
diff --git a/usr.sbin/pcvt/fed/Makefile b/usr.sbin/pcvt/fed/Makefile
new file mode 100644
index 0000000..ac1baa4
--- /dev/null
+++ b/usr.sbin/pcvt/fed/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= fed
+SRCS= fed.c select.c edit.c misc.c
+
+DPADD= ${LIBCURSES}
+LDADD= -lncurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/fed/edit.c b/usr.sbin/pcvt/fed/edit.c
new file mode 100644
index 0000000..1d7e4e6
--- /dev/null
+++ b/usr.sbin/pcvt/fed/edit.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * edit.c font editor edit character
+ * ------------------------------------------
+ *
+ * edit.c, 3.00, last edit-date: [Mon Mar 27 16:35:47 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+#define UP 0
+#define DOWN 1
+
+static int pen;
+
+/*---------------------------------------------------------------------------*
+ * fill edit mode command window
+ *---------------------------------------------------------------------------*/
+void edit_mode(void)
+{
+ mvwprintw(cmd_win,1,1,"(W)hite ");
+ mvwprintw(cmd_win,2,1,"(Black ");
+ mvwprintw(cmd_win,3,1,"(I)nvert ");
+ mvwprintw(cmd_win,4,1,"(R)ow BLACK ");
+ mvwprintw(cmd_win,5,1,"(r)ow WHITE ");
+ mvwprintw(cmd_win,6,1,"(C)ol BLACK ");
+ mvwprintw(cmd_win,7,1,"(c)ol WHITE ");
+ mvwprintw(cmd_win,8,1,"(Q)uit/Save ");
+
+ mvwprintw(cmd_win,9 ,1,"e(X)it/undo ");
+ mvwprintw(cmd_win,10,1,"Pen (U)p ");
+ mvwprintw(cmd_win,11,1,"Pen (D)own ");
+ mvwprintw(cmd_win,12,1," ");
+ mvwprintw(cmd_win,13,1,"(^P)rev Line");
+ mvwprintw(cmd_win,14,1,"(^N)ext Line");
+ mvwprintw(cmd_win,15,1,"(^F)orwd Col");
+ mvwprintw(cmd_win,16,1,"(^B)ack Col");
+ wrefresh(cmd_win);
+}
+
+/*---------------------------------------------------------------------------*
+ * edit mode command loop
+ *---------------------------------------------------------------------------*/
+int edit(void)
+{
+ int c, r;
+ char l;
+ unsigned int k_ch;
+
+ c = r = 0;
+
+ pen = UP;
+
+ for(;;)
+ {
+ if(pen == DOWN)
+ dis_cmd(" Edit Mode, the Pen is DOWN");
+ else
+ dis_cmd(" Edit Mode, the Pen is UP");
+
+ l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ wattron(ch_win,A_REVERSE);
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wattroff(ch_win,A_REVERSE);
+ wmove(ch_win,(r+1),(c+1));
+ wrefresh(ch_win);
+
+ k_ch = wgetch(ch_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_ch(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < (ch_height-1))
+ {
+ normal_ch(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_ch(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < (ch_width-1))
+ {
+ normal_ch(r,c);
+ c++;
+ }
+ break;
+
+ case KEY_HOME:
+ normal_ch(r,c);
+ c = r = 0;
+ break;
+
+ case KEY_LL:
+ normal_ch(r,c);
+ c = ch_width-1;
+ r = ch_height-1;
+ break;
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ case '\n':
+ case '\r':
+ case ' ' :
+ chg_pt(r,c);
+ break;
+
+ case 'q':
+ pen = UP;
+ normal_ch(r,c);
+ wrefresh(ch_win);
+ return(1);
+ break;
+
+ case 'x':
+ pen = UP;
+ normal_ch(r,c);
+ wrefresh(ch_win);
+ return(0);
+ break;
+
+ case 'w':
+ case 'W':
+ setchr(WHITE);
+ break;
+
+ case 'b':
+ case 'B':
+ setchr(BLACK);
+ break;
+
+ case 'i':
+ case 'I':
+ invert();
+ break;
+
+ case 'r':
+ setrow(WHITE);
+ break;
+
+ case 'R':
+ setrow(BLACK);
+ break;
+
+ case 'c':
+ setcol(WHITE);
+ break;
+
+ case 'C':
+ setcol(BLACK);
+ break;
+
+ case 'u':
+ case 'U':
+ pen = UP;
+ break;
+
+ case 'd':
+ case 'D':
+ pen = DOWN;
+ break;
+
+ default:
+ beep();
+ break;
+
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+void normal_ch(int r, int c)
+{
+ char l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ wattroff(ch_win,A_REVERSE);
+ if(pen == DOWN)
+ mvwprintw(ch_win,(r+1),(c+1),"*");
+ else
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wmove(ch_win,(r+1),(c+1));
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+void chg_pt(int r, int c)
+{
+ char l;
+ l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ if(l == WHITE)
+ l = BLACK;
+ else
+ l = WHITE;
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wmove(ch_win,(r+1),(c+1));
+}
+
+/*---------------------------------------------------------------------------*
+ * invert current character
+ *---------------------------------------------------------------------------*/
+void invert(void)
+{
+ int r,c;
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ while(c <= ch_width)
+ {
+ if(WHITE == mvwinch(ch_win, r, c))
+ mvwaddch(ch_win, r, c, BLACK);
+ else
+ mvwaddch(ch_win, r, c, WHITE);
+ c++;
+ }
+ r++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * fill current character black/white
+ *---------------------------------------------------------------------------*/
+void setchr(char type)
+{
+ int r,c;
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ while(c <= ch_width)
+ {
+ mvwaddch(ch_win, r, c, type);
+ c++;
+ }
+ r++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set current row to black/white
+ *---------------------------------------------------------------------------*/
+void setrow(char type)
+{
+ int r,c;
+
+ getyx(ch_win,r,c);
+
+ c = 1;
+
+ while(c <= ch_width)
+ {
+ mvwaddch(ch_win, r, c, type);
+ c++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set current column to black/white
+ *---------------------------------------------------------------------------*/
+void setcol(char type)
+{
+ int r,c;
+
+ getyx(ch_win,r,c);
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ mvwaddch(ch_win, r, c, type);
+ r++;
+ }
+}
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fed/fed.1 b/usr.sbin/pcvt/fed/fed.1
new file mode 100644
index 0000000..e5c4df3
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.1
@@ -0,0 +1,59 @@
+.\" Copyright (c) 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this 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.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 16:57:41 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt FED 8
+.Os
+.Sh NAME
+.Nm fed
+.Nd fonteditor for pcvt EGA/VGA font files
+.Sh SYNOPSIS
+.Nm
+filename
+.Sh DESCRIPTION
+The
+.Nm
+utility is a curses based fullscreen application which allows to edit
+.Xr pcvt 4
+fontfiles interactively.
+.Pp
+The
+.Nm
+utility displays a command window, a character display window and a
+character select window.
+.Pp
+In character select mode, it allows to move a character font to
+another position, exchange two character fonts or switch to edit
+character mode.
+.Pp
+In character edit mode, the user is able to edit the selected
+character font or apply several operations to it.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/fed/fed.c b/usr.sbin/pcvt/fed/fed.c
new file mode 100644
index 0000000..8c226b5
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * fed.c font editor main file
+ * -------------------------------------
+ *
+ * last edit-date: [Mon Mar 27 16:36:45 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#define FED
+
+#include "fed.h"
+
+void main(int argc, char *argv[])
+{
+ int i;
+ int row, col;
+ int ret;
+
+ if(argc != 2)
+ {
+ fprintf(stderr,"EGA/VGA Fonteditor, Rel 1.00\n");
+ fprintf(stderr,"usage: %s <fontfilename>\n",argv[0]);
+ exit(1);
+ }
+
+ readfont(argv[1]); /* read fontfile into memory */
+
+ initscr();
+ cbreak();
+ noecho();
+ nonl();
+ keypad(stdscr,TRUE);
+ idlok(stdscr, TRUE);
+
+ move(0,0);
+ standout();
+ addstr(" Interactive EGA/VGA Fonteditor - (c) 1993, 2000 Hellmuth Michaelis ");
+ standend();
+
+/* character horizontal ruler */
+
+ move(WINROW-1, CHCOL + ((WIDTH16 - ch_width)/2) + 1);
+ if(ch_width == WIDTH16)
+ addstr("1234567890123456");
+ else
+ addstr("12345678");
+
+/* charcater vertical ruler */
+
+ for(i=1; i < ch_height+1; i++)
+ mvprintw((WINROW+i), (CHCOL + ((WIDTH16 - ch_width)/2) - 2), "%2d", i);
+
+
+/* select horizontal ruler */
+
+ move(WINROW-1,SETCOL+2);
+ addstr("0 1 2 3 4 5 6 7 8 9 A B C D E F ");
+
+/* select vertical ruler */
+
+ for(i=0; i<10; i++)
+ mvaddch((WINROW+i+1),(SETCOL-1),(i+'0'));
+ for(i=0; i<6; i++)
+ mvaddch((WINROW+10+i+1),(SETCOL-1),(i+'A'));
+
+/* label available commands window */
+
+ move(WINROW-1,CMDCOL+1);
+ addstr("Commands");
+
+ refresh();
+
+/* command window */
+
+ cmd_win = newwin(((WSIZE)+(2*WBORDER)),(CMDSIZE+(2*WBORDER)),
+ WINROW,CMDCOL);
+ keypad(cmd_win,TRUE);
+ idlok(cmd_win, TRUE);
+ box(cmd_win,'|','-');
+
+ sel_mode();
+
+/* character font window */
+
+ ch_win = newwin((ch_height+(2*WBORDER)),(ch_width+(2*WBORDER)),
+ WINROW, CHCOL+((WIDTH16 - ch_width)/2));
+ keypad(ch_win,TRUE);
+ idlok(ch_win, TRUE);
+
+ box(ch_win,'|','-');
+ wrefresh(ch_win);
+
+/* character select window */
+
+ set_win = newwin((WSIZE+(2*WBORDER)),((WSIZE*2)+(2*WBORDER)),
+ WINROW,SETCOL); /* whole character set */
+ keypad(set_win,TRUE);
+ idlok(set_win, TRUE);
+
+ box(set_win,'|','-');
+
+ row = 0;
+ col = 0;
+
+ for(i=0; i<256; i++)
+ {
+ mvwprintw(set_win,row+1,col+1,"%02.2X",i);
+ if(++row > 15)
+ {
+ row = 0;
+ col += 2;
+ }
+ }
+ wmove(set_win,1,1);
+ wrefresh(set_win);
+
+/* start */
+
+ clr_cmd();
+
+ curchar = 0;
+
+ if((ret = selectc()) == 1)
+ {
+ writefont();
+ }
+ endwin();
+}
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fed/fed.h b/usr.sbin/pcvt/fed/fed.h
new file mode 100644
index 0000000..bf488ab
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ *
+ * last edit-date: [Mon Mar 27 16:37:27 2000]
+ *
+ * $FreeBSD$
+ */
+
+#include <ncurses.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef FED
+
+int ch_height;
+int ch_width;
+
+int curchar;
+
+WINDOW *ch_win;
+WINDOW *set_win;
+WINDOW *cmd_win;
+
+#else
+
+extern int ch_height; /* current fontfile character dimensions */
+extern int ch_width;
+
+extern int curchar; /* character being edited */
+
+extern WINDOW *ch_win; /* windows */
+extern WINDOW *set_win;
+extern WINDOW *cmd_win;
+
+#endif
+
+#define FONTCHARS 256 /* no of chars in a fontfile */
+
+#define WHITE ('.')
+#define BLACK ('*')
+
+#define K_UP 0x10 /* ^P */
+#define K_DOWN 0x0e /* ^N */
+#define K_RIGHT 0x06 /* ^F */
+#define K_LEFT 0x02 /* ^B */
+
+#define WINROW 3
+#define CMDCOL 3
+#define CHCOL 20
+#define SETCOL 41
+#define WSIZE 16
+#define CMDSIZE 12
+#define WBORDER 1
+
+/* fonts */
+
+#define WIDTH8 8 /* 8 bits wide font */
+#define WIDTH16 16 /* 16 bits wide font */
+
+#define FONT8X8 2048 /* filesize for 8x8 font */
+#define HEIGHT8X8 8 /* 8 scan lines char cell height */
+
+#define FONT8X10 2560 /* filesize for 8x10 font */
+#define HEIGHT8X10 10 /* 10 scan lines char cell height */
+
+#define FONT8X14 3584 /* filesize for 8x14 font */
+#define HEIGHT8X14 14 /* 14 scan lines char cell height */
+#define WIDTH8X14 8 /* 8 bits wide font */
+
+#define FONT8X16 4096 /* filesize for 8x16 font */
+#define HEIGHT8X16 16 /* 16 scan lines char cell height */
+
+#define FONT16X16 8192 /* filesize for 16x16 font */
+#define HEIGHT16X16 16 /* 16 scan lines char cell height */
+
+
+void edit_mode ( void );
+int edit ( void );
+void normal_ch ( int r, int c );
+void chg_pt ( int r, int c );
+void invert ( void );
+void setchr ( char type );
+void setrow ( char type );
+void setcol ( char type );
+void main ( int argc, char *argv[] );
+void readfont ( char *filename );
+void dis_cmd ( char *strg );
+void clr_cmd ( void );
+void save_ch ( void );
+void move_ch ( int src, int dest );
+void xchg_ch ( int src, int dest );
+void display ( int no );
+void sel_mode ( void );
+int selectc ( void );
+void normal_set ( int r, int c );
+int sel_dest ( void );
+void normal_uset ( int r, int c );
+void writefont( void );
+
+/* ------------------------------ EOF ----------------------------------- */
diff --git a/usr.sbin/pcvt/fed/misc.c b/usr.sbin/pcvt/fed/misc.c
new file mode 100644
index 0000000..5a0cbc9
--- /dev/null
+++ b/usr.sbin/pcvt/fed/misc.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * misc.c font editor misc routines
+ * -----------------------------------------
+ *
+ * last edit-date: [Mon Mar 27 16:38:12 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static unsigned char *fonttab; /* ptr to font in core memory */
+
+static char *bitmask[] = {
+ "....", /* 0 */
+ "...*", /* 1 */
+ "..*.", /* 2 */
+ "..**", /* 3 */
+ ".*..", /* 4 */
+ ".*.*", /* 5 */
+ ".**.", /* 6 */
+ ".***", /* 7 */
+ "*...", /* 8 */
+ "*..*", /* 9 */
+ "*.*.", /* A */
+ "*.**", /* B */
+ "**..", /* C */
+ "**.*", /* D */
+ "***.", /* E */
+ "****", /* F */
+ NULL };
+
+static char lfilename[1024]; /* current filename */
+static unsigned int lfilesize; /* current filename's size */
+
+/*---------------------------------------------------------------------------*
+ * read fontfile into memory
+ *---------------------------------------------------------------------------*/
+void readfont(char *filename)
+{
+ FILE *in;
+ struct stat sbuf, *sbp;
+ int ret;
+ char buffer[1024];
+
+ sbp = &sbuf;
+
+ if((in = fopen(filename, "r")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for reading", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((fstat(fileno(in), sbp)) != 0)
+ {
+ sprintf(buffer, "cannot fstat file %s", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ switch(sbp->st_size)
+ {
+ case FONT8X8:
+ ch_height = HEIGHT8X8;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X10:
+ ch_height = HEIGHT8X10;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X14:
+ ch_height = HEIGHT8X14;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X16:
+ ch_height = HEIGHT8X16;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT16X16:
+ ch_height = HEIGHT16X16;
+ ch_width = WIDTH16;
+ break;
+
+ default:
+ fprintf(stderr,"error, file %s is no valid font file, size=%d\n",filename,sbp->st_size);
+ exit(1);
+ }
+
+ if((fonttab = (unsigned char *)malloc((size_t)sbp->st_size)) == NULL)
+ {
+ fprintf(stderr,"error, malloc failed\n");
+ exit(1);
+ }
+
+ strcpy(lfilename, filename); /* save for write */
+ lfilesize = sbp->st_size; /* save for write */
+
+ if((ret = fread(fonttab, sizeof(*fonttab), sbp->st_size, in)) != sbp->st_size)
+ {
+ sprintf(buffer,"error reading file %s, size = %d, ret = %d\n",filename,sbp->st_size, ret);
+ perror(buffer);
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * write fontfile to disk
+ *---------------------------------------------------------------------------*/
+void writefont()
+{
+ FILE *in, *out;
+ int ret;
+ char buffer[1024];
+
+ if((in = fopen(lfilename, "r")) != NULL)
+ {
+ int c;
+ char wfn[1024];
+
+ strcpy(wfn, lfilename);
+ strcat(wfn, ".BAK");
+ if((out = fopen(wfn, "w")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for writing", wfn);
+ perror(buffer);
+ exit(1);
+ }
+
+ while(( c = fgetc(in) ) != EOF )
+ fputc(c, out);
+
+ fclose(out);
+ fclose(in);
+ }
+
+ if((out = fopen(lfilename, "w")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for writing", lfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((ret = fwrite(fonttab, sizeof(*fonttab), lfilesize, out)) != lfilesize)
+ {
+ sprintf(buffer,"error writing file %s, size=%d, ret=%d\n",lfilename,lfilesize, ret);
+ perror(buffer);
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * display a string
+ *---------------------------------------------------------------------------*/
+void dis_cmd(char *strg)
+{
+ move(22,0);
+ clrtoeol();
+ mvaddstr(22,0,strg);
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * clear a command string
+ *---------------------------------------------------------------------------*/
+void clr_cmd(void)
+{
+ move(22,0);
+ clrtoeol();
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * move char from src to dest
+ *---------------------------------------------------------------------------*/
+void move_ch(int src, int dst)
+{
+ unsigned char *s, *d;
+ int offset = 0;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * src]);
+ d = &(fonttab[ch_height * offset * dst]);
+
+ bcopy(s, d, (ch_height*offset)); /* src -> dst */
+}
+
+/*---------------------------------------------------------------------------*
+ * exchange char's src and dest
+ *---------------------------------------------------------------------------*/
+void xchg_ch(int src, int dst)
+{
+ unsigned char *s, *d;
+ unsigned char buf[32];
+ int offset = 0;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * src]);
+ d = &(fonttab[ch_height * offset * dst]);
+
+ bcopy(s, buf, (ch_height*offset)); /* src -> tmp */
+ bcopy(d, s, (ch_height*offset)); /* dst -> src */
+ bcopy(buf, d, (ch_height*offset)); /* tmp -> dst */
+}
+
+/*---------------------------------------------------------------------------*
+ * display the current selected character
+ *---------------------------------------------------------------------------*/
+void display(int no)
+{
+ unsigned char *fontchar;
+ char line[32];
+ int ln_no;
+ unsigned char hibyte;
+ unsigned char lobyte;
+ int offset;
+ int r;
+
+ offset = 0;
+ r = 1;
+ lobyte = 0;
+
+ if(ch_width == WIDTH16)
+ fontchar = &(fonttab[ch_height * 2 * no]);
+ else
+ fontchar = &(fonttab[ch_height * no]);
+
+ for (ln_no = 0; ln_no < ch_height; ln_no++)
+ {
+ hibyte = *(fontchar + (offset++));
+
+ if(ch_width == WIDTH16)
+ {
+ lobyte = *(fontchar + offset++);
+ }
+
+ strcpy(line,bitmask[(int)((hibyte >> 4) & 0x0f)]);
+ strcat(line,bitmask[(int)(hibyte & 0x0f)]);
+
+ if(ch_width == WIDTH16)
+ {
+ strcat(line,bitmask[(int)((lobyte >> 4) & 0x0f)]);
+ strcat(line,bitmask[(int)(lobyte & 0x0f)]);
+ mvwprintw(ch_win, r, 1, "%16.16s", line);
+ }
+ else
+ {
+ mvwprintw(ch_win, r, 1, "%8.8s", line);
+ }
+ r++;
+ }
+ wmove(ch_win, 1, 1);
+ wrefresh(ch_win);
+}
+
+/*---------------------------------------------------------------------------*
+ * save character
+ *---------------------------------------------------------------------------*/
+void save_ch(void)
+{
+ unsigned char *s;
+ int offset = 0;
+ int r, c;
+ unsigned short byte;
+ unsigned short shift;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * curchar]);
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ byte = 0;
+ if(offset == 2)
+ shift = 0x8000;
+ else
+ shift = 0x80;
+
+ while(c <= ch_width)
+ {
+ if(mvwinch(ch_win, r, c) == BLACK)
+ byte |= shift;
+ shift = (shift >> 1);
+ c++;
+ }
+ *s++ = byte;
+ r++;
+ }
+}
+
+/*---------------------------------- E O F ----------------------------------*/
+
+
diff --git a/usr.sbin/pcvt/fed/select.c b/usr.sbin/pcvt/fed/select.c
new file mode 100644
index 0000000..e4e44ca
--- /dev/null
+++ b/usr.sbin/pcvt/fed/select.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * select.c font editor select character
+ * ----------------------------------------------------
+ *
+ * last edit-date: [Mon Mar 27 16:38:50 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+int sc, sr, scurchar;
+
+int edit();
+
+void sel_mode(void)
+{
+ mvwprintw(cmd_win,1,1,"(E)dit ");
+ mvwprintw(cmd_win,2,1,"(M)ove ");
+ mvwprintw(cmd_win,3,1,"exchan(G)e ");
+ mvwprintw(cmd_win,4,1,"(Q)uit/Save ");
+ mvwprintw(cmd_win,5,1,"e(X)it/Undo ");
+ mvwprintw(cmd_win,6,1," ");
+ mvwprintw(cmd_win,7,1," ");
+ mvwprintw(cmd_win,8,1," ");
+
+ mvwprintw(cmd_win,9 ,1," ");
+ mvwprintw(cmd_win,10,1," ");
+ mvwprintw(cmd_win,11,1," ");
+ mvwprintw(cmd_win,12,1," ");
+ mvwprintw(cmd_win,13,1,"(^P)rev Line");
+ mvwprintw(cmd_win,14,1,"(^N)ext Line");
+ mvwprintw(cmd_win,15,1,"(^F)orwd Col");
+ mvwprintw(cmd_win,16,1,"(^B)ack Col");
+ wrefresh(cmd_win);
+}
+
+int selectc()
+{
+ int c, r;
+ int ret;
+ char h, l;
+ unsigned int k_ch;
+
+ c = (curchar / 16);
+ r = (curchar % 16);
+
+ for(;;)
+ {
+ dis_cmd(" Select Character");
+
+ sel_mode();
+
+ curchar = r + (c*16);
+
+ display(curchar);
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattron(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_REVERSE);
+ wmove(set_win,(r+1),((c*2)+1));
+ wrefresh(set_win);
+
+ k_ch = wgetch(set_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_set(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < 15)
+ {
+ normal_set(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_set(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < 15)
+ {
+ normal_set(r,c);
+ c++;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ edit_mode();
+ dis_cmd(" Edit Character");
+ display(curchar);
+ ret = edit();
+ if(ret == 1)
+ save_ch();
+ break;
+
+ case 'g':
+ case 'G':
+ dis_cmd(" Exchange: select Destination, then press RETURN or any other Key to ABORT");
+ sr = r;
+ sc = c;
+ scurchar = curchar;
+ if((curchar = sel_dest()) == -1)
+ { /* failsafe */
+ r = sr;
+ c = sc;
+ curchar = scurchar;
+ }
+ else
+ { /* valid return */
+ normal_set(r,c);
+ c = (curchar / 16);
+ r = (curchar % 16);
+ xchg_ch(scurchar,curchar);
+ }
+ break;
+
+ case 'm':
+ case 'M':
+ dis_cmd(" Move: select Destination, then press RETURN or any other Key to ABORT");
+ sr = r;
+ sc = c;
+ scurchar = curchar;
+ if((curchar = sel_dest()) == -1)
+ { /* failsafe */
+ r = sr;
+ c = sc;
+ curchar = scurchar;
+ }
+ else
+ { /* valid return */
+ normal_set(r,c);
+ c = (curchar / 16);
+ r = (curchar % 16);
+ move_ch(scurchar,curchar);
+ }
+ break;
+
+ case 'q':
+ case 'Q':
+ normal_set(r,c);
+ wrefresh(set_win);
+ return(1);
+ break;
+
+ case 'x':
+ case 'X':
+ normal_set(r,c);
+ wrefresh(set_win);
+ return(0);
+ break;
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ default:
+ beep();
+ break;
+
+ }
+ }
+}
+
+void normal_set(int r, int c)
+{
+ char h, l;
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattroff(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wmove(set_win,(r+1),((c*2)+1));
+}
+
+int sel_dest(void)
+{
+ int c, r;
+ char h, l;
+ unsigned int k_ch;
+
+ c = (curchar / 16);
+ r = (curchar % 16);
+
+ for(;;)
+ {
+
+ curchar = r + (c*16);
+
+ display(curchar);
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattron(set_win,A_UNDERLINE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_UNDERLINE);
+ wmove(set_win,(r+1),((c*2)+1));
+ wrefresh(set_win);
+
+ k_ch = wgetch(set_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_uset(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < 15)
+ {
+ normal_uset(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_uset(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < 15)
+ {
+ normal_uset(r,c);
+ c++;
+ }
+ break;
+
+ case '\r':
+ case '\n':
+ normal_uset(r,c);
+ return(r + (c*16));
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ default:
+ normal_uset(r,c);
+ return(-1);
+ }
+ }
+}
+
+void normal_uset(int r, int c)
+{
+ char h, l;
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+
+ wattroff(set_win,A_UNDERLINE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wmove(set_win,(r+1),((c*2)+1));
+
+ if((r==sr) && (c==sc))
+ {
+ wattron(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_REVERSE);
+ wmove(set_win,(r+1),((c*2)+1));
+ }
+}
+
+
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fontedit/Makefile b/usr.sbin/pcvt/fontedit/Makefile
new file mode 100644
index 0000000..4851169
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= fontedit
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/fontedit/README b/usr.sbin/pcvt/fontedit/README
new file mode 100644
index 0000000..1854129
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/README
@@ -0,0 +1,36 @@
+When I first saw this posted to rn, I tried to compile this on a machine
+running BSD UNIX. Much to my dissapointment, It said "unable to find
+/usr/include/termio.h" and thus it sat for a couple months. I was able to
+compile it on a 3b5, but I didn't have a vt220 hooked up to it. I was doing
+some unrelated work with ioctl calls and finally realized that it would not be
+too hard to convert it from System V to BSD. It also looked kind of strange
+with the cursor on, so I turned this off. To implement this, compile with
+the "-DCURFIX" flag in the Makefile.
+I am working on a new version that uses curses and that would enable you to
+change the file that your are working on without leaving the program.
+I thought I'd post it as it is now, since it has a lot of uses right away.
+Imagine changing your favorite game to have objects that look like what they
+are instead of the regular characters. Also, I think people should post their
+own character sets, if they come up with some neat stuff.
+ Please send any comments or suggestions to:
+
+ UUCP : ..!harvard!bu-cs!bucsb!eap
+ ARPANET: eap@bucsb.bu.edu
+ CSNET : eap%bucsb@bu-cs
+
+ Have fun,
+
+ - Eric Pearce
+ Boston University
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/usr.sbin/pcvt/fontedit/fontedit.1 b/usr.sbin/pcvt/fontedit/fontedit.1
new file mode 100644
index 0000000..d0b7515
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.1
@@ -0,0 +1,92 @@
+.\" $FreeBSD$
+.\"
+.Dd January 16, 2001
+.Dt FONTEDIT 1
+.Os
+.Sh NAME
+.Nm fontedit
+.Nd "edit fonts"
+.Sh SYNOPSIS
+.Nm
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to edit the down line reloadable character set (DRCS) of a
+.Tn VT220
+terminal.
+The editor has two display areas, one for displaying the
+entry currently being manipulated, and one for displaying the complete
+DRCS.
+Commands to the editor take the form of function keys.
+.Pp
+The
+.Nm
+utility
+takes one command line parameter, a
+.Ar file
+name.
+This file is used to save the character set.
+If the file exists when
+.Nm
+is invoked, it is read in to initialize the DRCS.
+The file is written to when
+.Nm
+exits.
+.Pp
+Commands to
+.Nm
+take the form of function keys.
+The current definitions are:
+.Bl -tag -width "Cursors"
+.It Ic HELP
+Display a help screen.
+.It Ic F6
+Turn the pixel under the cursor on.
+.It Ic F7
+Turn the pixel under the cursor off.
+.It Ic F13
+Clear the display area.
+.It Ic Find
+Save the current font in the font table.
+Update the DRCS display.
+.It Ic Select
+Extract the entry selected by the cursor in the DRCS display.
+.It Ic Prev
+Move the cursor to the previous entry in the DRCS display.
+.It Ic Next
+Move the cursor to the next entry in the DRCS display.
+.It Ic Insert
+Insert a blank line at the current cursor position.
+The bottom row is lost.
+.It Ic Remove
+Remove the row at the current cursor position.
+All rows below the
+current one are shifted up.
+.It Ic Cursors
+Move the cursor in the main display area.
+.El
+.Pp
+If the screen gets garbled, press
+.Aq control\-L .
+.Pp
+To exit
+.Nm ,
+press
+.Aq control\-D .
+The DRCS will be saved in
+.Ar file .
+To exit without saving the DRCS, hit interrupt (usually
+DEL).
+.Sh DIAGNOSTICS
+The
+.Nm
+utility
+will issue a warning when the entry being worked on is not saved, and
+some potentially destructive command, like
+.Ic Select
+is used.
+To
+override the warning message, immediately reissue the command.
+.Sh AUTHORS
+.An Greg Franks
diff --git a/usr.sbin/pcvt/fontedit/fontedit.c b/usr.sbin/pcvt/fontedit/fontedit.c
new file mode 100644
index 0000000..b1444c6
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.c
@@ -0,0 +1,926 @@
+/*
+ * fontedit
+ * Fonteditor for VT220
+ *
+ * BUGS:
+ * o Cursor motion is less than optimal (but who cares at 9600),
+ *
+ * COMPILE:
+ * cc -O fontedit.c -o fontedit
+ * (use Makefile)
+ *
+ * Copyright (c) 1987 by Greg Franks.
+ *
+ * Permission is granted to do anything you want with this program
+ * except claim that you wrote it.
+ *
+ *
+ * REVISION HISTORY:
+ *
+ * Nov 21, 1987 - Fixed man page to say "Fontedit" instead of "Top"
+ * Nov 22, 1987 - Added BSD Compatible ioctl, turned cursor on/off
+ * - eap@bucsf.bu.edu
+ * $FreeBSD$
+ */
+
+void clear_screen();
+#include <stdio.h>
+#ifdef SYSV
+#include <sys/termio.h>
+#endif /* SYSV */
+#ifdef BSD
+#include <sys/ioctl.h>
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#endif /* __NetBSD__ || __FreeBSD__ */
+#include <signal.h>
+
+#ifdef CURFIX
+#define CURSORON "\033[?25h"
+#define CURSOROFF "\033[?25l"
+#endif /* CURFIX */
+
+#define MAX_ROWS 10
+#define MAX_COLS 8
+
+typedef enum { false, true } bool;
+
+#define KEY_FIND 0x0100
+#define KEY_INSERT 0x0101
+#define KEY_REMOVE 0x0102
+#define KEY_SELECT 0x0103
+#define KEY_PREV 0x0104
+#define KEY_NEXT 0x0105
+#define KEY_F6 0X0106
+#define KEY_F7 0x0107
+#define KEY_F8 0x0108
+#define KEY_F9 0x0109
+#define KEY_F10 0x010a
+#define KEY_F11 0x010b
+#define KEY_F12 0x010c
+#define KEY_F13 0x010d
+#define KEY_F14 0x010e
+#define KEY_HELP 0x010f
+#define KEY_DO 0x0110
+#define KEY_F17 0x0111
+#define KEY_F18 0x0112
+#define KEY_F19 0x0113
+#define KEY_F20 0x0114
+#define KEY_UP 0x0115
+#define KEY_DOWN 0x0116
+#define KEY_RIGHT 0x0117
+#define KEY_LEFT 0x0118
+
+/*
+ * Position of main drawing screen.
+ */
+
+#define ROW_OFFSET 3
+#define COL_OFFSET 10
+
+/*
+ * Position of the DRCS table.
+ */
+
+#define TABLE_ROW 4
+#define TABLE_COL 50
+
+/*
+ *
+ */
+
+#define ERROR_ROW 20
+#define ERROR_COL 40
+
+bool display_table[MAX_ROWS][MAX_COLS];
+
+#define TOTAL_ENTRIES (128 - 32)
+#define SIXELS_PER_CHAR 16
+
+char font_table[TOTAL_ENTRIES][SIXELS_PER_CHAR];
+unsigned int current_entry;
+
+#ifdef SYSV
+struct termio old_stty, new_stty;
+#endif /* SYSV */
+#ifdef BSD
+struct sgttyb old_stty, new_stty;
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+struct termios old_stty, new_stty;
+#endif /* __NetBSD__ || __FreeBSD__ */
+FILE * font_file = (FILE *)0;
+
+
+/*
+ * Interrupt
+ * Exit gracefully.
+ */
+
+interrupt()
+{
+ void clear_screen();
+#ifdef CURFIX
+ printf("%s\n",CURSORON);
+#endif /* CURFIX */
+#ifdef SYSV
+ ioctl( 0, TCSETA, &old_stty );
+#endif /* SYSV */
+#ifdef BSD
+ ioctl( 0, TIOCSETP, &old_stty );
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCSETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ clear_screen();
+ exit( 0 );
+}
+
+
+/*
+ * Main
+ * Grab input/output file and call main command processor.
+ */
+
+main( argc, argv )
+int argc;
+char *argv[];
+{
+ void command(), init_restore(), clear_screen();
+ void save_table(), get_table(), extract_entry();
+
+ if ( argc != 2 ) {
+ fprintf( stderr, "usage: fontedit filename\n" );
+ exit( 1 );
+ }
+
+ printf( "Press HELP for help\n" );
+ printf( "\033P1;1;2{ @\033\\" ); /* Clear font buffer */
+ fflush( stdout );
+ sleep( 1 ); /* Let terminal catch up */
+ /* otherwise we get frogs */
+
+ if ( ( font_file = fopen( argv[1], "r" ) ) == (FILE *)0 ) {
+ if ( ( font_file = fopen( argv[1], "w" ) ) == (FILE *)0 ) {
+ fprintf( stderr, "Cannot create file %s \n", argv[1] );
+ exit( 1 );
+ }
+ }
+ fclose( font_file );
+
+ if ( ( font_file = fopen( argv[1], "r" ) ) != (FILE *)0 ) {
+ get_table( font_file );
+ fclose( font_file );
+ }
+
+ if ( ( font_file = fopen( argv[1], "r+" ) ) == (FILE *)0 ) {
+ fprintf( stderr, "Cannot open %s for writing\n", argv[1] );
+ exit( 1 );
+ }
+#ifdef CURFIX
+ printf("%s\n",CURSOROFF);
+#endif /* CURFIX */
+#ifdef SYSV
+ ioctl( 0, TCGETA, &old_stty );
+#endif /* SYSV */
+#ifdef BSD
+ ioctl( 0, TIOCGETP, &old_stty );
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCGETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ signal( SIGINT, (void *) interrupt );
+ new_stty = old_stty;
+#ifdef SYSV
+ new_stty.c_lflag &= ~ICANON;
+ new_stty.c_cc[VMIN] = 1;
+ ioctl( 0, TCSETA, &new_stty );
+#endif /* SYSV */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ new_stty.c_lflag &= ~ICANON;
+ new_stty.c_lflag &= ~ECHO;
+ new_stty.c_cc[VMIN] = 1;
+ ioctl( 0, TIOCSETA, &new_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+#ifdef BSD
+ new_stty.sg_flags |= CBREAK;
+ new_stty.sg_flags &= ~ECHO;
+ ioctl( 0, TIOCSETP, &new_stty );
+#endif /* BSD */
+ current_entry = 1;
+ extract_entry( current_entry );
+ init_restore();
+ command();
+#ifdef SYSV
+ ioctl( 0, TCSETA, &old_stty );
+#endif /* SYSV */
+#ifdef BSD
+ ioctl( 0, TIOCSETP, &old_stty );
+#endif /* BSD */
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCSETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ clear_screen();
+
+ /* Overwrite the old file. */
+
+ fseek( font_file, 0L, 0 );
+ save_table( font_file );
+ fclose( font_file );
+#ifdef CURFIX
+ printf("%s\n",CURSORON);
+#endif /* CURFIX */
+}
+
+
+
+/*
+ * Command
+ * Process a function key.
+ *
+ * The user cannot fill in slots 0 or 95 (space and del respecitively).
+ */
+
+void
+command()
+{
+ register int c;
+ register int row, col;
+ register int i, j;
+ bool change, error, override;
+
+ void build_entry(), extract_entry(), send_entry(), print_entry();
+ void highlight(), draw_current(), init_restore(), help();
+ void warning();
+
+ change = false;
+ error = false;
+ override = false;
+ row = 0; col = 0;
+ highlight( row, col, true );
+
+ for ( ;; ) {
+ c = get_key();
+ highlight( row, col, false ); /* turn cursor off */
+
+ if ( error ) {
+ move ( ERROR_ROW, ERROR_COL );
+ printf( "\033[K" ); /* Clear error message */
+ move ( ERROR_ROW+1, ERROR_COL );
+ printf( "\033[K" ); /* Clear error message */
+ error = false;
+ } else {
+ override = false;
+ }
+
+ switch ( c ) {
+
+ case KEY_FIND: /* update DRCS */
+ if ( !change && !override ) {
+ warning( "No changes to save" );
+ override = true;
+ error = true;
+ } else {
+ build_entry( current_entry );
+ send_entry( current_entry );
+ print_entry( current_entry, true );
+ change = false;
+ }
+ break;
+
+ case KEY_F6: /* Turn on pixel */
+ change = true;
+ display_table[row][col] = true;
+ highlight( row, col, false );
+ col = ( col + 1 ) % MAX_COLS;
+ if ( col == 0 )
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_F7: /* Turn off pixel */
+ change = true;
+ display_table[row][col] = false;
+ highlight( row, col, false );
+ col = ( col + 1 ) % MAX_COLS;
+ if ( col == 0 )
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_INSERT: /* Insert a blank row */
+ change = true;
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = MAX_ROWS - 1; i > row; --i ) {
+ display_table[i][j] = display_table[i-1][j];
+ }
+ display_table[row][j] = false;
+ }
+ draw_current();
+ break;
+
+ case KEY_REMOVE: /* Remove a row */
+ change = true;
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = row; i < MAX_ROWS - 1; ++i ) {
+ display_table[i][j] = display_table[i+1][j];
+ }
+ display_table[MAX_ROWS-1][j] = false;
+ }
+ draw_current();
+ break;
+
+ case KEY_F13: /* Clear buffer */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ error = true;
+ override = true;
+ } else {
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = 0; i < MAX_ROWS; ++i ) {
+ display_table[i][j] = false;
+ }
+ }
+ draw_current();
+ }
+ break;
+
+ case KEY_SELECT: /* Select font from DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ error = true;
+ override = true;
+ } else {
+ extract_entry( current_entry );
+ draw_current();
+ }
+ break;
+
+ case KEY_PREV: /* Move to prev entry in DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ override = true;
+ error = true;
+ } else {
+ print_entry( current_entry, false );
+ current_entry = current_entry - 1;
+ if ( current_entry == 0 )
+ current_entry = TOTAL_ENTRIES - 2;
+ print_entry( current_entry, true );
+ }
+ break;
+
+ case KEY_NEXT: /* Move to next entry in DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ override = true;
+ error = true;
+ } else {
+ print_entry( current_entry, false );
+ current_entry = current_entry + 1;
+ if ( current_entry == TOTAL_ENTRIES - 1 )
+ current_entry = 1;
+ print_entry( current_entry, true );
+ }
+ break;
+
+ case KEY_UP: /* UP one row. */
+ if ( row == 0 )
+ row = MAX_ROWS;
+ row = row - 1;
+ break;
+
+ case KEY_DOWN: /* Guess. */
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_RIGHT:
+ col = ( col + 1 ) % MAX_COLS;
+ break;
+
+ case KEY_LEFT:
+ if ( col == 0 )
+ col = MAX_COLS;
+ col = col - 1;
+ break;
+
+ case KEY_HELP: /* Display helpful info */
+ clear_screen();
+ help();
+ c = getchar();
+ init_restore();
+ break;
+
+ case '\004': /* All done! */
+ return;
+
+ case '\f': /* Redraw display */
+ init_restore();
+ break;
+
+ default: /* user is a klutzy typist */
+ move ( ERROR_ROW, ERROR_COL );
+ printf( "Unknown key: " );
+ if ( c < 0x20 ) {
+ printf( "^%c", c );
+ } else if ( c < 0x0100 ) {
+ printf( "%c", c );
+ } else {
+ printf( "0x%04x", c );
+ }
+ fflush( stdout );
+ error = true;
+ }
+
+ highlight( row, col, true ); /* turn cursor on */
+ }
+}
+
+
+
+char *key_table[] = {
+ "\033[1~", /* Find */
+ "\033[2~", /* Insert */
+ "\033[3~", /* Remove */
+ "\033[4~", /* Select */
+ "\033[5~", /* Prev */
+ "\033[6~", /* Next */
+ "\033[17~",
+ "\033[18~",
+ "\033[19~",
+ "\033[20~",
+ "\033[21~",
+ "\033[23~",
+ "\033[24~",
+ "\033[25~",
+ "\033[26~",
+ "\033[28~",
+ "\033[29~",
+ "\033[31~",
+ "\033[32~",
+ "\033[33~",
+ "\033[34~",
+ "\033[A",
+ "\033[B",
+ "\033[C",
+ "\033[D",
+ (char *)0 };
+
+/*
+ * get_key
+ * Convert VT220 escape sequence into something more reasonable.
+ */
+
+int
+get_key()
+{
+ register char *p;
+ char s[10];
+ register int i, j;
+
+ p = s;
+ for ( i = 0; i < 10; ++i ) {
+ *p = getchar();
+ if ( i == 0 && *p != '\033' )
+ return( (int)*p ); /* Not an escape sequence */
+ if ( *p != '\033' && *p < 0x0020 )
+ return( (int)*p ); /* Control character */
+ *++p = '\0'; /* Null terminate */
+ for ( j = 0; key_table[j]; ++j ) {
+ if ( strcmp( s, key_table[j] ) == 0 ) {
+ return( j | 0x0100 );
+ }
+ }
+ }
+ return( -1 );
+}
+
+
+
+/*
+ * pad
+ * Emit nulls so that the terminal can catch up.
+ */
+
+pad()
+{
+ int i;
+
+ for ( i = 0; i < 20; ++i )
+ putchar( '\000' );
+ fflush( stdout );
+}
+
+
+
+/*
+ * init_restore
+ * refresh the main display table.
+ */
+
+void
+init_restore()
+{
+ register int row, col;
+ register int i;
+
+ void draw_current(), clear_screen(), print_entry();
+
+ clear_screen();
+
+ for ( col = 0; col < MAX_COLS; ++col ) {
+ move( ROW_OFFSET - 2, col * 3 + COL_OFFSET + 1 );
+ printf( "%d", col );
+ }
+ move( ROW_OFFSET - 1, COL_OFFSET );
+ printf( "+--+--+--+--+--+--+--+--+" );
+ move( ROW_OFFSET + MAX_ROWS * 2, COL_OFFSET );
+ printf( "+--+--+--+--+--+--+--+--+" );
+
+ for ( row = 0; row < MAX_ROWS; ++row ) {
+ if ( row != 0 && row != 7 ) {
+ move( row * 2 + ROW_OFFSET, COL_OFFSET - 2 );
+ printf( "%d|", row );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET - 1 );
+ printf( "|" );
+ move( row * 2 + ROW_OFFSET, COL_OFFSET + MAX_COLS * 3 );
+ printf( "|" );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET + MAX_COLS * 3 );
+ printf( "|" );
+ } else {
+ move( row * 2 + ROW_OFFSET, COL_OFFSET - 2 );
+ printf( "%d*", row );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET - 1 );
+ printf( "*" );
+ move( row * 2 + ROW_OFFSET, COL_OFFSET + MAX_COLS * 3 );
+ printf( "*" );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET + MAX_COLS * 3 );
+ printf( "*" );
+ }
+ }
+ draw_current();
+
+ move( TABLE_ROW - 1, TABLE_COL - 1 );
+ printf( "+-+-+-+-+-+-+-+-+-+-+-+-+" );
+ move( TABLE_ROW + 8 * 2 - 1, TABLE_COL - 1 );
+ printf( "+-+-+-+-+-+-+-+-+-+-+-+-+" );
+ for ( i = 0; i < 8; ++i ) {
+ move ( TABLE_ROW + i * 2, TABLE_COL - 1 );
+ printf( "|" );
+ move ( TABLE_ROW + i * 2 + 1, TABLE_COL - 1 );
+ printf( "+" );
+ move ( TABLE_ROW + i * 2, TABLE_COL + 12 * 2 - 1);
+ printf( "|" );
+ move ( TABLE_ROW + i * 2 + 1, TABLE_COL +12 * 2 - 1);
+ printf( "+" );
+ }
+ for ( i = 0; i < TOTAL_ENTRIES; ++i )
+ print_entry( i, (i == current_entry) ? true : false );
+}
+
+
+
+/*
+ * draw_current
+ * Draw the complete current entry.
+ */
+
+void
+draw_current()
+{
+ register int row, col;
+
+ printf( "\033)0" ); /* Special graphics in G1 */
+ printf( "\016" ); /* Lock in G1 (SO) */
+
+ for ( row = 0; row < MAX_ROWS; ++row ) {
+ for ( col = 0; col < MAX_COLS; ++col ) {
+ if ( display_table[row][col] ) {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ } else {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ }
+ }
+ pad();
+ }
+ printf( "\017" ); /* Lock in G0 (SI) */
+ fflush( stdout );
+}
+
+
+
+/*
+ * highlight
+ * Draw the cursor in the main display area.
+ */
+
+void
+highlight( row, col, on )
+unsigned int row, col;
+bool on;
+{
+
+ printf( "\033)0" ); /* Special graphics in G1 */
+ printf( "\016" ); /* Lock in G1 (SO) */
+ if ( on ) {
+ printf( "\033[7m" ); /* Reverse video cursor */
+ }
+
+ if ( display_table[row][col] ) {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ } else {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ }
+ pad();
+ printf( "\017" ); /* Lock in G0 (SI) */
+ printf( "\033[0m" ); /* normal video */
+ printf( "\b" ); /* Back up one spot */
+ fflush( stdout );
+}
+
+
+
+/*
+ * Clear_screen
+ */
+
+void
+clear_screen()
+{
+ printf( "\033[H\033[J" ); /* Clear screen. */
+ fflush( stdout );
+}
+
+
+
+/*
+ * move
+ */
+
+move( y, x )
+int y, x;
+{
+ printf( "\033[%d;%df", y, x );
+}
+
+
+
+/*
+ * Build_entry
+ * Convert the bit pattern used in the main display area into something
+ * that the vt220 can digest - namely sixels...
+ */
+
+void
+build_entry( entry_no )
+unsigned int entry_no;
+{
+ register int row, col;
+ register unsigned int mask;
+
+ for ( col = 0; col < 8; ++col ) {
+
+ /* Top set of sixels */
+
+ mask = 0;
+ for ( row = 5; row >= 0; --row ) {
+ mask = mask << 1;
+ if ( display_table[row][col] )
+ mask |= 1;
+ }
+ font_table[entry_no][col] = mask + 077;
+
+ /* Bottom set of sixels */
+
+ mask = 0;
+ for ( row = 9; row >= 6; --row ) {
+ mask = mask << 1;
+ if ( display_table[row][col] )
+ mask |= 1;
+ }
+ font_table[entry_no][col+8] = mask + 077;
+ }
+
+}
+
+
+
+/*
+ * Extract_engry
+ * convert sixel representation into an array of bits.
+ */
+
+void
+extract_entry( entry_no )
+unsigned int entry_no;
+{
+ register int row, col;
+ register unsigned int mask;
+
+ for ( col = 0; col < 8; ++col ) {
+
+ /* Top set of sixels */
+
+ mask = font_table[entry_no][col];
+ if ( mask >= 077 )
+ mask -= 077;
+ else
+ mask = 0; /* Bogus entry */
+
+ for ( row = 0; row <= 5; ++row ) {
+ display_table[row][col] = (bool)(mask & 0x0001);
+ mask = mask >> 1;
+ }
+
+ /* Bottom set of sixels */
+
+ mask = font_table[entry_no][col+8];
+ if ( mask >= 077 )
+ mask -= 077;
+ else
+ mask = 0;
+
+ for ( row = 6; row <= 9; ++row ) {
+ display_table[row][col] = (bool)(mask & 0x0001);
+ mask = mask >> 1;
+ }
+ }
+
+}
+
+
+
+/*
+ * Send_entry
+ * Emit the stuff used by the VT220 to load a character into the
+ * DRCS. We could, of course, send more than one entry at a time...
+ */
+
+void
+send_entry( entry_no )
+int entry_no;
+{
+ register char *fp = font_table[entry_no];
+
+ printf( "\033P1;%d;1;0;0;0{ @%c%c%c%c%c%c%c%c/%c%c%c%c%c%c%c%c\033\\",
+ entry_no,
+ fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7],
+ fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15] );
+}
+
+
+
+/*
+ * Print_entry
+ * The terminal normally has G0 in GL. We don't want to change
+ * this, nor do we want to use GR. Sooooo send out the necessary
+ * magic for shifting in G2 temporarily for the character that we
+ * want to display.
+ */
+
+void
+print_entry( entry_no, highlight )
+register unsigned int entry_no;
+bool highlight;
+{
+
+ register int y, x;
+
+ y = entry_no & 0x07;
+ x = entry_no >> 3 & 0x1f;
+ entry_no += 32; /* Map up to G set */
+
+ move( y * 2 + TABLE_ROW, x * 2 + TABLE_COL );
+
+ if ( highlight )
+ printf( "\033[7m" );
+
+ printf( "\033* @" ); /* select DRCS into G2 */
+ printf( "\033N" ); /* select single shift */
+ printf( "%c", entry_no ); /* Draw the character */
+
+ if ( highlight )
+ printf( "\033[0m" );
+}
+
+
+
+/*
+ * Save_table
+ * Save a font table
+ */
+
+void
+save_table( font_file )
+FILE *font_file;
+{
+ register char *fp;
+ register int i;
+
+ for ( i = 0; i < TOTAL_ENTRIES; ++i ) {
+ fp = font_table[i];
+ fprintf( font_file, "\033P1;%d;1;0;0;0{ @%c%c%c%c%c%c%c%c/%c%c%c%c%c%c%c%c\033\\\n",
+ i,
+ fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7],
+ fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15] );
+ }
+}
+
+
+
+/*
+ * Get_table
+ * Extract font table entries from a file
+ */
+
+void
+get_table( font_file )
+FILE *font_file;
+{
+ char s[256];
+ register char *p;
+ char *fp;
+ int i;
+ register int j;
+
+ while( fgets( s, 255, font_file ) ) {
+ if ( strncmp( s, "\033P1;", 4 ) != 0 )
+ continue; /* Bogus line */
+ p = &s[4];
+ if ( sscanf( p, "%d", &i ) != 1 )
+ continue; /* Illegal entry number */
+
+ if ( i <= 0 || TOTAL_ENTRIES <= i )
+ continue; /* Bogues entry */
+
+ fp = font_table[i];
+
+ while ( *p && *p != '@' )
+ ++p; /* Skip to font definition */
+ if ( ! *p++ )
+ continue; /* Skip @ */
+
+ for ( j = 0; *p && *p != '\033' && j < 16; ++j, ++p ) {
+ if ( *p == '/' ) {
+ j = 8;
+ ++p;
+ }
+ fp[j] = *p;
+ }
+ send_entry( i );
+ }
+}
+
+
+
+/*
+ * Help
+ * Print out help information.
+ */
+
+void
+help()
+{
+ printf( "Font editor\n\n" );
+ printf( "F6 - Pixel on\n" );
+ printf( "F7 - Pixel off\n" );
+ printf( "F13 - Clear display area\n" );
+ printf( "HELP - This screen\n" );
+ printf( "FIND - Update font table\n" );
+ printf( "INSERT - Insert a blank row\n" );
+ printf( "REMOVE - Remove a row\n" );
+ printf( "SELECT - Select current font table entry\n" );
+ printf( "PREV - Move to previous font table entry\n" );
+ printf( "NEXT - Move to next font table entry\n" );
+ printf( "^D - Exit\n" );
+ printf( "\n\n\n\nPress any key to continue\n" );
+}
+
+
+
+/*
+ * Warning
+ * Issue a warning to the regarding the current status.
+ */
+
+void
+warning( s )
+char *s;
+{
+ move( ERROR_ROW, ERROR_COL );
+ printf( "Warning: %s!\n", s );
+ move( ERROR_ROW+1, ERROR_COL );
+ printf( " Reissue command to override\n" );
+}
diff --git a/usr.sbin/pcvt/fonts/COPYRIGHT b/usr.sbin/pcvt/fonts/COPYRIGHT
new file mode 100644
index 0000000..175f7b8
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/COPYRIGHT
@@ -0,0 +1,38 @@
+The font files:
+
+ vt100pc.814.uu, vt100sg.814.uu, vt220h.808.uu, vt220h.810.uu,
+ vt220h.814.uu, vt220h.816.uu, vt220l.808.uu, vt220l.810.uu,
+ vt220l.814.uu and vt220l.816.uu
+
+in this directory are
+
+ Copyright (c) 1992, 1993, 1994 Hellmuth Michaelis and 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.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by
+ Hellmuth Michaelis and Joerg Wunsch
+ 4. The name 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 ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED 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.
+
diff --git a/usr.sbin/pcvt/fonts/Makefile b/usr.sbin/pcvt/fonts/Makefile
new file mode 100644
index 0000000..8af01cc
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.SUFFIXES: .uu
+.uu:
+ rm -f ${.TARGET}
+ uudecode ${.IMPSRC}
+
+FILES= vt220h.808 vt220h.810 vt220h.814 vt220h.816 \
+ vt220l.808 vt220l.810 vt220l.814 vt220l.816
+#FILES+= vt100pc.814 vt100sg.814
+FILESDIR= ${FONTDIR}
+CLEANFILES= ${FILES}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/fonts/vt100pc.814.uu b/usr.sbin/pcvt/fonts/vt100pc.814.uu
new file mode 100644
index 0000000..470f9c9
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt100pc.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt100pc.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP`````
+M```````````````````8/#P\&!@`&!@`````8V-C(@``````````````-C9_
+M-C8V?S8V````#`P^8V%@/@-#8SX,#```````86,&#!@S8P``````'#8V'#MN
+M9F8[`````#`P,&````````````````P8,#`P,#`8#```````&`P&!@8&!@P8
+M`````````&8\_SQF```````````8&!C_&!@8````````````````&!@8,```
+M````````_P`````````````````````8&````````0,&#!@P8$`````````^
+M8V=O>W-C8SX```````P</`P,#`P,/P``````/F,#!@P8,&-_```````^8P,#
+M'@,#8SX```````8.'C9F?P8&#P``````?V!@8'X#`V,^```````<,&!@?F-C
+M8SX``````']C`P8,&!@8&```````/F-C8SYC8V,^```````^8V-C/P,#!CP`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@```````^8V,&#`P`#`P``````#YC
+M8V]O;VY@/@``````"!PV8V-_8V-C``````!^,S,S/C,S,WX``````!XS86!@
+M8&$S'@``````?#8S,S,S,S9\``````!_,3$T/#0Q,7\``````'\Q,30\-#`P
+M>```````'C-A8&!O8S,=``````!C8V-C?V-C8V,``````#P8&!@8&!@8/```
+M````#P8&!@8&9F8\``````!S,S8V/#8V,W,``````'@P,#`P,#$Q?P``````
+MP^?_V\/#P\/#``````!C<WM_;V=C8V,``````!PV8V-C8V,V'```````?C,S
+M,SXP,#!X```````^8V-C8VMO/@8'`````'XS,S,^-C,S<P``````/F-C,!P&
+M8V,^``````#_F9D8&!@8&#P``````&-C8V-C8V-C/@``````P\/#P\/#9CP8
+M``````##P\/#V]O_9F8``````,/#9CP8/&;#PP``````P\/#9CP8&!@\````
+M``#_@X8,&#!AP?\``````#PP,#`P,#`P/```````0&!P.!P.!P,!```````\
+M#`P,#`P,##P````('#9C`````````````````````````````/\`&!@,````
+M`````````````````#P&/F9F.P``````<#`P/#8S,S-N```````````^8V!@
+M8SX```````X&!AXV9F9F.P``````````/F-_8&,^```````<-C(P?#`P,'@`
+M`````````#MF9F8^!F8\````<#`P-CLS,S-S```````,#``<#`P,#!X`````
+M``8&``X&!@8&9F8\````<#`P,S8\-C-S```````<#`P,#`P,#!X`````````
+M`.;_V]O;VP``````````;C,S,S,S```````````^8V-C8SX``````````&XS
+M,S,^,#!X````````.V9F9CX&!@\```````!N.S,P,'@``````````#YC.`YC
+M/@``````"!@8?A@8&!L.``````````!F9F9F9CL``````````,/#PV8\&```
+M````````P\/;V_]F``````````!C-AP<-F,``````````&-C8V,_`P8\````
+M````?V8,&#-_```````.&!@8<!@8&`X``````!@8&!@`&!@8&```````<!@8
+M&`X8&!AP```````[;@``````````````J@"J`*H`J@"J`*H`J@``/&;"P,#"
+M9CP,!GP```#,S`#,S,S,S'8`````#!@P`'S&_L#&?``````0.&P`>`Q\S,QV
+M``````#,S`!X#'S,S'8`````8#`8`'@,?,S,=@`````X;#@`>`Q\S,QV````
+M`````'C,P,QX&`QX````$#AL`'S&_L#&?```````S,P`?,;^P,9\`````&`P
+M&`!\QO[`QGP``````,S,`'`P,#`P>``````P>,P`<#`P,#!X`````,!@,`!P
+M,#`P,'@`````QL80.&S&QO[&Q@```#AL.``X;,;&_L;&````&#!@`/YB8'Q@
+M8OX`````````;+H2?I"8?@``````/FS,S/[,S,S.`````!`X;`!\QL;&QGP`
+M`````,;&`'S&QL;&?`````!@,!@`?,;&QL9\`````#!XS`#,S,S,S'8`````
+M8#`8`,S,S,S,=@``````QL8`QL;&QGX&#'@``,;&.&S&QL;&;#@`````QL8`
+MQL;&QL;&?``````("'[(R,C(?@@(`````#AL9&#P8&!@YOP``````,9L.!#^
+M$/X0$`````#XS,SXQ,S>S,SF`````!PT,#`P_#`P,#"PX```&#!@`'@,?,S,
+M=@`````,&#``.!@8&!@\`````!@P8`!\QL;&QGP`````&#!@`,S,S,S,=@``
+M````=MP`W&9F9F9F````=MP`QN;V_M[.QL8`````/&QL/@!^```````````X
+M;&PX`'P````````````P,``P,&#&QGP```````````!_8&!@````````````
+M`'\!`0$``````,#`QLS8,&#<A@P8/@``P,#&S-@P9LZ:/@8&````&!@`&!@\
+M/#P8`````````#9LV&PV````````````V&PV;-@``````"*((H@BB"*((H@B
+MB"*(JE2J5*I4JE2J5*I4JE2Z[KKNNNZZ[KKNNNZZ[A@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&/@8^!@8&!@8&#8V-C8V-C;V-C8V-C8V````
+M`````/XV-C8V-C8``````/@8^!@8&!@8&#8V-C8V]@;V-C8V-C8V-C8V-C8V
+M-C8V-C8V-C8``````/X&]C8V-C8V-C8V-C8V]@;^````````-C8V-C8V-OX`
+M```````8&!@8&/@8^`````````````````#X&!@8&!@8&!@8&!@8&!\`````
+M```8&!@8&!@8_P````````````````#_&!@8&!@8&!@8&!@8&!\8&!@8&!@`
+M````````_P```````!@8&!@8&!C_&!@8&!@8&!@8&!@?&!\8&!@8&!@V-C8V
+M-C8V-S8V-C8V-C8V-C8V-S`_```````````````_,#<V-C8V-C8V-C8V-O<`
+M_P``````````````_P#W-C8V-C8V-C8V-C8W,#<V-C8V-C8``````/\`_P``
+M`````#8V-C8V]P#W-C8V-C8V&!@8&!C_`/\````````V-C8V-C8V_P``````
+M````````_P#_&!@8&!@8`````````/\V-C8V-C8V-C8V-C8V/P```````!@8
+M&!@8'Q@?```````````````?&!\8&!@8&!@`````````/S8V-C8V-C8V-C8V
+M-C;_-C8V-C8V&!@8&!C_&/\8&!@8&!@8&!@8&!@8^``````````````````?
+M&!@8&!@8__________________\`````````__________#P\/#P\/#P\/#P
+M\/#P#P\/#P\/#P\/#P\/#P__________````````````````=MS8V-QV````
+M`````'S&_,;&_,#`0````/[&QL#`P,#`P`````````#^;&QL;&QL``````#^
+MQF`P&#!@QOX``````````'[8V-C8<`````````!F9F9F?&!@P````````';<
+M&!@8&!@``````/PP>,S,S'@P_```````.&S&QO[&QFPX```````X;,;&QFQL
+M;.X``````#Q@,!A\S,S,>```````````?-;6?``````````&#'S.UN9\8,``
+M`````#A@P,#XP,!@.````````'S&QL;&QL;&````````_@``_@``_@``````
+M`#`P,/PP,#``_```````8#`8#!@P8`#\```````8,&#`8#`8`/P```````X:
+M&A@8&!@8&!@8&!@8&!@8&!@86%AP```````P,```_```,#``````````=MP`
+M=MP```````!PV-AP````````````````````&!@`````````````````&```
+M```````>&!@8&!C8V'@X`````-AL;&QL;```````````<-@P8,CX````````
+=```````^/CX^/CX`````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt100sg.814.uu b/usr.sbin/pcvt/fonts/vt100sg.814.uu
new file mode 100644
index 0000000..24ea4c7
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt100sg.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt100sg.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP`````
+M```````````````````8/#P\&!@`&!@`````8V-C(@``````````````-C9_
+M-C8V?S8V````#`P^8V%@/@-#8SX,#```````86,&#!@S8P``````'#8V'#MN
+M9F8[`````#`P,&````````````````P8,#`P,#`8#```````&`P&!@8&!@P8
+M`````````&8\_SQF```````````8&!C_&!@8````````````````&!@8,```
+M````````_P`````````````````````8&````````0,&#!@P8$`````````^
+M8V=O>W-C8SX```````P</`P,#`P,/P``````/F,#!@P8,&-_```````^8P,#
+M'@,#8SX```````8.'C9F?P8&#P``````?V!@8'X#`V,^```````<,&!@?F-C
+M8SX``````']C`P8,&!@8&```````/F-C8SYC8V,^```````^8V-C/P,#!CP`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@```````^8V,&#`P`#`P``````#YC
+M8V]O;VY@/@``````"!PV8V-_8V-C``````!^,S,S/C,S,WX``````!XS86!@
+M8&$S'@``````?#8S,S,S,S9\``````!_,3$T/#0Q,7\``````'\Q,30\-#`P
+M>```````'C-A8&!O8S,=``````!C8V-C?V-C8V,``````#P8&!@8&!@8/```
+M````#P8&!@8&9F8\``````!S,S8V/#8V,W,``````'@P,#`P,#$Q?P``````
+MP^?_V\/#P\/#``````!C<WM_;V=C8V,``````!PV8V-C8V,V'```````?C,S
+M,SXP,#!X```````^8V-C8VMO/@8'`````'XS,S,^-C,S<P``````/F-C,!P&
+M8V,^``````#_F9D8&!@8&#P``````&-C8V-C8V-C/@``````P\/#P\/#9CP8
+M``````##P\/#V]O_9F8``````,/#9CP8/&;#PP``````P\/#9CP8&!@\````
+M``#_@X8,&#!AP?\``````#PP,#`P,#`P/```````0&!P.!P.!P,!```````\
+M#`P,#`P,##P````('#9C```````````````````````````````````0.'S^
+M_GPX$```````_H*"_H*"_H*"_@````!$1'Q$1``^"`@("````'Q`>$!\`#X@
+M."`@````/$!`0#P`/"(\)"(```!`0$!`?``^(#P@(````'#8V'``````````
+M`````#`P,/PP,#``_`````!$9%1,1``@("`@/@```$1$1"@0`#X("`@(```8
+M&!@8&!@8^`````````````````#X&!@8&!@8`````````!\8&!@8&!@8&!@8
+M&!@8'P```````!@8&!@8&!C_&!@8&!@8``#_````````````````````_P``
+M`````````````````/\```````````````````#_````````````````````
+M_P```!@8&!@8&!@?&!@8&!@8&!@8&!@8&/@8&!@8&!@8&!@8&!@8_P``````
+M``````````#_&!@8&!@8&!@8&!@8&!@8&!@8&!@``!@P8,!@,!@`_```````
+M8#`8#!@P8`#\`````````/YL;&QL;&P````````"!'X($'X@0``````X;&1@
+M\&!@8.;\`````````````!@`````````J@"J`*H`J@"J`*H`J@``````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````&!@`&!@\/#P8``````@(?LC(R,A^"`@`````.&QD8/!@8&#F_```
+M``````````````````````#&;#@0$/X0$!````````````````````````!\
+MQF`X;,;&;#@,QGP```"!F3QF9F8\F8$``````'R"FJ*BHIJ"?``````\;&P^
+M`'X``````````````#9LV&PV````````````````````````````````````
+M```````````````````````````````````````````````````\)"0\````
+M````````&!A^&!@`?@```````'S&!@0,&##&_@``````?,8&!CP&!L9\````
+M````````````````````````9F9F9GQ@8,``````?O3T]'04%!04````````
+M```8&```````````````````````````````"!@X&!@8&!A^`````#AL;#@`
+M?```````````````V&PV;-@```````#`P,;,V#!FSIH^!@8``,#`QLS8,&#<
+MA@P8/@``````-FS8;#8`````````,#``,#!@QL9\````,!@`$#ALQL;^QL8`
+M```,&``0.&S&QO[&Q@```!!LQA`X;,;&_L;&````=MP`$#ALQL;^QL8`````
+MQL80.&S&QO[&Q@```#AL.``X;,;&_L;&```````^;,S,_LS,S,X``````#QF
+MPL#`PF8\#`9\`#`8#`#^8F!\8&+^````&#!@`/YB8'Q@8OX````0;,8`_F)@
+M?&!B_@````#&Q@#^8F!\8&+^````,!@`/!@8&!@8&#P````,&``\&!@8&!@8
+M/````#QF`#P8&!@8&!@\````9F8`/!@8&!@8&#P`````````````````````
+M`';<`,;F]O[>SL;&````8#``.&S&QL;&;#@````,&``X;,;&QL9L.````!!L
+M@CALQL;&QFPX````=MP`.&S&QL;&;#@`````QL8X;,;&QL9L.```````?LS,
+MS,[,S,Q^```````".FS.UM;F;+B```!@,!@`QL;&QL;&?`````8,&`#&QL;&
+MQL9\`````';<`,;&QL;&QGP`````QL8`QL;&QL;&?````,;&`,;&;#@0$!`X
+M`````````````````````````'S&QMS&QOS`P,````!@,!@`>`Q\S,QV````
+M`!@P8`!X#'S,S'8`````$#AL`'@,?,S,=@``````=MP`>`Q\S,QV``````#,
+MS`!X#'S,S'8`````.&PX`'@,?,S,=@````````!LNA)^D)A^`````````'C,
+MP,QX&`QX````8#`8`'S&_L#&?``````,&#``?,;^P,9\`````!`X;`!\QO[`
+MQGP``````,S,`'S&_L#&?`````#`8#``<#`P,#!X``````P8,``X&!@8&#P`
+M````,'C,`'`P,#`P>```````S,P`<#`P,#!X````````````````````````
+M`';<`-QF9F9F9@````!@,!@`?,;&QL9\`````!@P8`!\QL;&QGP`````.&S&
+M`'S&QL;&?```````=MP`?,;&QL9\``````#&Q@!\QL;&QGP``````````'R2
+MDIR0?@````````)\SM;6YGR``````&`P&`#,S,S,S'8`````&#!@`,S,S,S,
+M=@`````P>,P`S,S,S,QV``````#,S`#,S,S,S'8``````,;&`,;&QL9^!@QX
+=````````````````````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.808.uu b/usr.sbin/pcvt/fonts/vt220h.808.uu
new file mode 100644
index 0000000..c9585dc
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.808.uu
@@ -0,0 +1,49 @@
+begin 644 vt220h.808
+M9%1,1"`@(#X\0#@$>"(^(CQ`.`1X-@@V?$!X0'PV"#9\0'A`?#X("'Q`>'P<
+M(BH<.$1\1"PP*"1X1'A$>"`@/GA$>$1^$`P\1$1\1#X("`A`0$!\/B`\($1$
+M*!`^"`@(?$!X0#X@/"`\0$`\/"(\)CQ`.`1X'"(</$`X!'@<"!QX1$1X("`@
+M/GA$1'@$#`0.>$1X'"(,$#YX1'@<`@P"''A$1'@,$CX"9%1,(B0X)"(\0#@$
+M>"(<"'Q`>'P\(CP^/$`\(C(J)B)\0'A`?"(V*CQ`.`1X(B(<?$!X0'P>(!Y\
+M0'A`'B`>/#Q`3#P>(!X\>$1X3!X@'CQ$1#@>(!P"/'"(<(AP#!(,<(APB'`,
+M!`YPB'"(?"0(/'"(<(QB#@(<<(APC'@2/@)PB'"><!P"''"(<(YP/"(<<(AP
+MCG($"`APB'"<<APB''"(<)QR#@(<<(APB'P2'A)PB'"<<AP2''"(<(AP#A`.
+M<(APB'`<$AQPB'"><!P0'G"(<)YP'!`0<(AX"'`,$@QPB'@(<`P$#G"(>`A\
+M)`@\<(AX#&(.`AQPB'@,>!(^`G"(>!YP'`(<<(AX#G`\(AQPB'@><@0("'"(
+M>!QR'"(<<(AX''(.`AQPB'@(?!(>$G"(>!QR'!(<<(AX"'`.$`YPB'@(<!P2
+M''"(>!YP'!`><(AX'G`<$!```'S&QOS`P````,945'P8`'S&!G[&?```8+`8
+M+$V&`````!@X&!H,````_+8V-@0``'SD<.1\````PJ08*L8``$1$*"@0$```
+M$!`H*$1$``#&QL;&QGP```#\!@8&_````'[`P,!^````PD(D&"08`/Z"*#BJ
+M_@``QE14?!@\`````/YL;.X````$_@'^!````"1^@7XD````$!`H1.X`.$2"
+MJKJJ1#@``-QV`/X``/Z"Q,1H:#``&"PL1$:&_@`````VS,PV```8&`!F9@``
+M`!PT,#`PL.`!`@0($"!`@(!`(!`(!`(!`0($"!`@0/\``#\0"`0"`1@8,&`P
+M&!@8&!@,!@P8&!@```X8&!@8&!@8&!@8&`X```!P&!@8&!@8&!@8&!AP```"
+M`@(B$@H&``#NQM;6?```9F8\&'X8`%@D&"0X8`QX`!@`QL9^#'@`P,#XS/C`
+MP%`@4`P\9CP`\&!X;'A@\``.`&9F/!@\````1"@0*$0`_&9F]F9F_`#P$'(6
+M_CINP@``````&`QX&#!@````````````_@```'R"NJJRJH)\`````'@`````
+M``!X"`@``,;&``````````($?AA^($````````#_````````_P```````/\`
+M``````#_````````_P````````#N9F8L&````GS.UN9\@```?-;<T'P``/X`
+M?,;&?`#^`'@,?,QV`,P`S$AX,'@`.&P`QL;&?``<`,;&QL9\`'``QL;&QGP`
+M`CILUM9LN(``?LS.S,Q^`/XX;,;&;#@`.,8X;,9L.``.`#ALQFPX`'``.&S&
+M;#@`9@`\&!@8/``8)``\&!@\``X`/!@8&#P`<``\&!@8/`#&`/YB>&+^`#A$
+M_F)X8OX`<`#^8GAB_@#^`#ALQO[&`#AL$&S&_L8`#@`X;,;^Q@!P`#ALQO[&
+M```<#!X`````````&!@````('CIB>#P$&'@,.`QX````?(*:HJ*:@GP`P]L\
+M9CS;PP``S%1@4$P``'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+M?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``
+M,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&
+M8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#``
+M`'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P
+M`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\
+MQF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P
+M``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@
+M,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+M?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``
+M,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&
+M8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#``
+M`'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P
+M`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\
+MQF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P
+M``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@
+M,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+7?,9@,``P``!\QF`P`#```'S&8#``,`!@
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.810.uu b/usr.sbin/pcvt/fonts/vt220h.810.uu
new file mode 100644
index 0000000..04689b1
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.810.uu
@@ -0,0 +1,60 @@
+begin 644 vt220h.810
+M1&143$0@("`@/CQ`.`1X(B(^(B(\0#@$>"(4"!0B?$!X0'PB%`@4(GQ`>$!\
+M/@@("`A\0'A`?!PB(BH<.$1\1$0D*#`H)'A$>$1X("`@(#YX1'A$>!X@'`(\
+M1$1\1$0^"`@("$!`0$!\/B`\("!$1$0H$#X("`@(?$!X0$`^(#P@(#Q`0$`\
+M/"(\)"(\0#@$>!PB(B(</$`X!'@<"`@(''A$1$1X("`@(#YX1$1$>`0,!`0.
+M>$1$1'@<(@P0/GA$1$1X'`(,`AQX1$1$>`0($CX"1&143$0B)#@D(CQ`.`1X
+M(A0("`A\0'A`?#PB/"(\/$!`0#PB,BHF(GQ`>$!\(C8J(B(\0#@$>"(B(B(<
+M?$!X0'P>("`@'GQ`>$!`'B`<`CP\0$Q$/!X@'`(\>$1X2$0>(!P"/$1$1$0X
+M'B`<`CQPB'"(<`P2$A(,<(APB'`$#`0$#G"(<(AP'"(,$#YPB'"(<!P"#`(<
+M<(APB'`$"!(^`G"(<(AP'A`<`AQPB'"(<!P@/"(<<(APB'`^`@0("'"(<(AP
+M'"(<(AQPB'"(<!PB'@(<<(APB'`,$AX2$G"(<(AP'!(<$AQPB'"(<`X0$!`.
+M<(APB'`<$A(2''"(<(AP'A`<$!YPB'"(<!X0'!`0<(AX"'`,$A(2#'"(>`AP
+M!`P$!`YPB'@(<!PB#!`^<(AX"'`<`@P"''"(>`AP!`@2/@)PB'@(<!X0'`(<
+M<(AX"'`<(!PB''"(>`AP/@($"`APB'@(<!PB'"(<<(AX"'`<(AX"''"(>`AP
+M#!(>$A)PB'@(<!P2'!(<<(AX"'`.$!`0#G"(>`AP'!(2$AQPB'@(<!X0'!`>
+M<(AX"'`>$!P0$```?,;&QOS`P,````#&5%14?!@8`'S&!@9^QL9\``!@L#`8
+M&"Q-A@`````8.!@8&!H,````_+8V-C8&!`````!\Y'#D?`````#"I!@8*L8`
+M``""@D1$*"@0$```$!`H*$1$@H(``,;&QL;&QGP`````_`8&!@;\`````'[`
+MP,#`?@```,)")!@8)!@```#^@B@X*(+^``#&5%14?#@0.````/YL;&QL;.X`
+M````!/X!_@0``````"1^@7XD`````!`0*"A$1.X``#A$@JJZJH)$.````-QV
+M`/X``````/Z"Q,1H:#```!@8+"Q$1H;^```````V2$@V`````!@8``!F9@``
+M`!PT,#`P,+#@```!`@0($"!`@```@$`@$`@$`@$```$"!`@0($#_`````#\0
+M"`0"`0`8&!@P8#`8&!@8&!@8#`8,&!@8&```#A@8&!@8&!@8&!@8&!@8&`X`
+M``!P&!@8&!@8&!@8&!@8&!@8<`````("`B(2"@8``````.[&UM9\``!F9CP8
+M&'X8&`!8)!@8)#A@>`1X`!@P`,;&QGX,>`#`P/C,S/C`P```4"!0&`P\9F8\
+M`/!@>&QX8&#P``8,`&9F/!@8/`````!$*!`H1````/QF9O9F9F;\`/`0<!;N
+M&C9JSH(`````````&`QX&#!@````````````````_@``````?(*ZJKJRJH)\
+M``````!X``````````!X"`@```#&Q@````````````($?@@0?B!`````````
+M`````/\`````````_P````````#_`````````/\`````````_P``````````
+M````[FYF9BP8```"?,[6UN9\@`````!\UM;<T'P``';<`'S&QL9\`';<`'@,
+M?,S,=@#,`,S,2'@P,'@`.&P`QL;&QL9\``P8`,;&QL;&?``P&`#&QL;&QGP`
+M`CILSM;F;+B```!^S,S.S,S,?@!VS#ALQL;&;#@`.,8X;,;&QFPX``X`.&S&
+MQL9L.`!P`#ALQL;&;#@`9@`\&!@8&!@\`!@D`#P8&!@8/``,&``\&!@8&#P`
+M,!@`/!@8&!@\`,8`_F9B>&!B_@`X;`#^8GA@8OX`,!@`_F)X8&+^`';<`!`X
+M;,;^Q@`X;``0.&S&_L8`#!@`$#ALQO[&`&`P`!`X;,;^Q@``#!P,#!X`````
+M`````!@8```````('CID8'@\!!@`>`PX#'@``````'R"FJ*BHIJ"?`##VSQF
+M9CS;PP``````S%1@4$P``'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+H`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P`#`P
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.814.uu b/usr.sbin/pcvt/fonts/vt220h.814.uu
new file mode 100644
index 0000000..9d49552
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt220h.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP```!P
+MB'"(<``,$A(2#````'"(<(AP``0,!`0.````<(APB'``'"(,$#X```!PB'"(
+M<``<`@P"'````'"(<(AP``0($CX"````<(APB'``'A`<`AP```!PB'"(<``<
+M(#PB'````'"(<(AP`#X"!`@(````<(APB'``'"(<(AP```!PB'"(<``<(AX"
+M'````'"(<(AP``P2'A(2````<(APB'``'!(<$AP```!PB'"(<``.$!`0#@``
+M`'"(<(AP`!P2$A(<````<(APB'``'A`<$!X```!PB'"(<``>$!P0$````'"(
+M>`AP``P2$A(,````<(AX"'``!`P$!`X```!PB'@(<``<(@P0/@```'"(>`AP
+M`!P"#`(<````<(AX"'``!`@2/@(```!PB'@(<``>$!P"'````'"(>`AP`!P@
+M'"(<````<(AX"'``/@($"`@```!PB'@(<``<(APB'````'"(>`AP`!PB'@(<
+M````<(AX"'``#!(>$A(```!PB'@(<``<$AP2'````'"(>`AP``X0$!`.````
+M<(AX"'``'!(2$AP```!PB'@(<``>$!P0'@```'"(>`AP`!X0'!`0````````
+M`'S&QL;&_,#`P``````8V]O;V]M^&!@8````?,8&!G[&QGP``````,#`8#`8
+M'#9C00`````````P<#`P,#08``````````#<9F9F9F8&!@8``````'S&\,#&
+M?`````````#BMAP8.&S&````````@L;&;&PX.!`````````0.#AL;,;&@@``
+M`````,;&QL;&QL9\`````````/P&!@8&_```````````?L#`P,!^````````
+M``#&9CP8/&8\``````#^Q@!L?&P`QOX``````#P8V]O;VWX8/```````_FQL
+M;&QL;&SN````````"`S^`_X,"``````````D9G[#?F8D```````````0$"@H
+M1$3N```````\9L/G_^?#9CP``````````-QV`/X``````````/[&QLQL:#@P
+M````````&#@L;$3&QOX``````````&[8V&X````````````8&```9F8`````
+M```<-#`P,#`PL.`````````!`@0($"!`@````````(!`(!`(!`(!````````
+M``$"!`@0('\````````````_$`@$`@$``!@8&!@8,&`P&!@8&!@8&!@8&!@,
+M!@P8&!@8&!@```X8&!@8&!@8&!@8&!@8&!@8&!@8&!@.``````!P&!@8&!@8
+M&!@8&!@8&!@8&!@8&!@8<````````@("`B(2"@8"``````````!LQM;6UFP`
+M`````,S,>#`P_#`P,```````X'S@P'C`P&`X#'@```P8,`#&QL;&?@8,>```
+M`,#`^,S,^,#`P`````#8<.`P&`P^9F8\``````#P8'AL>&!@8/`````,&`#,
+MS,QX,#`P>`````````!$*!`H1`````````#\9F9F]F9F9OP```#P$'`6_!@P
+M9LZ:/@8&```````````````8#'@`&#!@``````````````````````#^````
+M````````?(*ZJKJRJH)\````````````>````````````````'@("```````
+M`,;&`````````````````````@9^"!!^8$```````````````````/\`````
+M`````````/\``````````````/\``````````````/\``````````````/\`
+M``````````````````#N;F9F;#@````````&?,[>]N9\P```````````?-;6
+MW-!^``````!VW`!\QL;&QGP``````';<`'@,?,S,=@```,S,`,S,2'@P,#!X
+M````.&S&`,;&QL;&QGP````&#!@`QL;&QL;&?````&`P&`#&QL;&QL9\````
+M```&/FS.UM;F;/C``````'[,S,S.S,S,?@```';<`#ALQL;&QFPX````.&S&
+M.&S&QL;&;#@````,&``X;,;&QL9L.````&`P`#ALQL;&QFPX````9F8`/!@8
+M&!@8&#P````\9@`\&!@8&!@8/`````P8`#P8&!@8&!@\````,!@`/!@8&!@8
+M&#P```#&Q@#^9F)X8&)F_@```#ALQ@#^9F!X8&;^````,!@,`/YF8'A@9OX`
+M``!VW``0.&S&QO[&Q@```#ALQA`X;,;&_L;&````#!@`$#ALQL;^QL8````P
+M&``0.&S&QO[&Q@`````,'`P,'@`````````````````8&````````````.`\
+M8,#`P,!@.`QX```\!AP&/`````````````!\@IJBHJ*:@GP```````##VSQF
+M9CS;PP``````````YFQX;&9F``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+M````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````
+M?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&
+M8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P
+M`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P
+M``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P````
+M``!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\
+MQL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@
+M,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``
+M,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``
+M`````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+M`'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&
+MQF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P
+M,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+M````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````
+M?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&
+M8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P
+M`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P
+M``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P````
+M``!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\
+MQL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@
+M,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``
+M,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``
+M`````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+M`'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&
+MQF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P
+M,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+=````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.816.uu b/usr.sbin/pcvt/fonts/vt220h.816.uu
new file mode 100644
index 0000000..401db37
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.816.uu
@@ -0,0 +1,95 @@
+begin 644 vt220h.816
+M``!$9%1,1``@("`@/@``````/$`X!'@`(B(^(B(``````#Q`.`1X`"(4"!0B
+M``````!\0'A`?``B%`@4(@``````?$!X0'P`/@@("`@``````'Q`>$!\`!PB
+M(BH<`@`````X1'Q$1``D*#`H)```````>$1X1'@`("`@(#X``````'A$>$1X
+M`!X@'`(\``````!$1'Q$1``^"`@("```````0$!`0'P`/B`\("```````$1$
+M1"@0`#X("`@(``````!\0'A`0``^(#P@(```````/$!`0#P`/"(\)"(`````
+M`#Q`.`1X`!PB(B(<```````\0#@$>``<"`@('```````>$1$1'@`("`@(#X`
+M`````'A$1$1X``0,!`0.``````!X1$1$>``<(@P0/@``````>$1$1'@`'`(,
+M`AP``````'A$1$1X``0($CX"``````!$9%1,1``B)#@D(@``````/$`X!'@`
+M(A0("`@``````'Q`>$!\`#PB/"(\```````\0$!`/``B,BHF(@``````?$!X
+M0'P`(C8J(B(``````#Q`.`1X`"(B(B(<``````!\0'A`?``>("`@'@``````
+M?$!X0$``'B`<`CP``````#Q`3$0\`!X@'`(\``````!X1'A(1``>(!P"/```
+M````1$1$1#@`'B`<`CP`````<(APB'``#!(2$@P``````'"(<(AP``0,!`0.
+M``````!PB'"(<``<(@P0/@``````<(APB'``'`(,`AP``````'"(<(AP``0(
+M$CX"``````!PB'"(<``>$!P"'```````<(APB'``'"`\(AP``````'"(<(AP
+M`#X"!`@(``````!PB'"(<``<(APB'```````<(APB'``'"(>`AP``````'"(
+M<(AP``P2'A(2``````!PB'"(<``<$AP2'```````<(APB'``#A`0$`X`````
+M`'"(<(AP`!P2$A(<``````!PB'"(<``>$!P0'@``````<(APB'``'A`<$!``
+M`````'"(>`AP``P2$A(,``````!PB'@(<``$#`0$#@``````<(AX"'``'"(,
+M$#X``````'"(>`AP`!P"#`(<``````!PB'@(<``$"!(^`@``````<(AX"'``
+M'A`<`AP``````'"(>`AP`!P@/"(<``````!PB'@(<``^`@0("```````<(AX
+M"'``'"(<(AP``````'"(>`AP`!PB'@(<``````!PB'@(<``,$AX2$@``````
+M<(AX"'``'!(<$AP``````'"(>`AP``X0$!`.``````!PB'@(<``<$A(2'```
+M````<(AX"'``'A`<$!X``````'"(>`AP`!X0'!`0`````````````#YC8V-C
+M?F!@8&```````!C;V]O;VWX8&!@8`````'S&!@9^QL9\`````````,#`8#`8
+M'#9C00```````````#!P,#`P-!@`````````````W&9F9F9F!@8&!@``````
+M``!\Y'#D?````````````.:V'!@X;,8```````````""QD1L*#@0````````
+M````$#@H;$3&@@````````#&QL;&QL;&QGP``````````/P&!@8&_```````
+M``````!^P,#`P'X``````````````,9F/!@\9CP```````#^Q@!L?'QL`,;^
+M```````\&-O;V]O;?A@8/````````/YL;&QL;&QL;.X```````````@,_@/^
+M#`@````````````D9G[#?F8D````````````$!`X*&Q$1.X````````X1(*J
+MNKJJ@D0X````````````W'8`_@``````````````_H+$Q&AH,#``````````
+M`!@8+"Q$1H;^````````````=HB(=@``````````````&!@``&9F````````
+M```,'A@8&!@8&'@P```````````!`@0($"!`@```````````@$`@$`@$`@$`
+M```````````!`@0($"!_```````````````_$`@$`@$````8&!@8&!@P8#`8
+M&!@8&!@8&!@8&!@8#`8,&!@8&!@8&```#A@8&!@8&!@8&!@8&!@8&!@8&!@8
+M&!@8&`X```````!P&!@8&!@8&!@8&!@8&!@8&!@8&!@8&!@8<``````````"
+M`@("(A(*!@(``````````````&S&UM9L````````9F9F/!@8?A@8&```````
+M<#YP8#Q@8#`<!CP`````````#!@P`,;&QL9^!@QX````P,#XS,S,^,#`P```
+M`````+!@X+`8##YF9CP```````#P8'QF9GQ@8&#P``````P8`,S,S'@P,#`P
+M>```````````1"@0*$0```````````#\9F9F]F9F9F;\`````.`0<!#F#!@P
+M9LJ2/@(``````````````````!@,>```&#!@````````````````````````
+M``#^`````````````'R"NJJJLJJJ@GP``````````````'@`````````````
+M`````'@("`````````#&Q@```````````````````````@9^"!!^8$``````
+M````````````````_P````````````````#_`````````````````/\`````
+M````````````_P````````````````#_````````````````````````YF9B
+M8C08```````````&?,[>]N9\P`````````````!\UM;<T'X`````````=MP`
+M?,;&QL9\`````````';<`'@,?,S,=@````#,S`#,S,QX,#`P,'@`````.&S&
+M`,;&QL;&QL9\``````8,&`#&QL;&QL;&?`````!@,!@`QL;&QL;&QGP`````
+M```&/FS.WO;F;/C`````````?MC8V-[>V-C8?@````!VW``X;,;&QL;&;#@`
+M````.&S&.&S&QL;&QFPX``````P8`#ALQL;&QL9L.`````!@,``X;,;&QL;&
+M;#@`````9F8`/!@8&!@8&!@\`````!@\9@`\&!@8&!@8/``````&#!@`/!@8
+M&!@8&#P`````8#`8`#P8&!@8&!@\`````,;&`/YB8&!\8&!B_@`````X;,8`
+M_F)@?&!@8OX`````8#`8`/YB8'Q@8&+^`````';<`!`X;,;&_L;&Q@`````X
+M;,80.&S&QO[&QL8`````&#!@$#ALQL;^QL;&`````#`8#!`X;,;&_L;&Q@``
+M````"!@("!P`````````````````````&!@`````````````X#Q@P,#`8#@,
+M>```````.`08!#@```````````````!\@IJBHJ*BFH)\``````````##VSQF
+M9CS;PP``````````````YFYX;&8```````!\QL9@,#`P`#`P````````````
+M`````````````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````
+M````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P
+M````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P
+M`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@
+M,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\
+MQL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``````
+M``!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``
+M``````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``
+M,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P
+M,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&
+MQF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```````
+M`'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```
+M`````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P
+M,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P
+M,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&
+M8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````
+M````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P
+M````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P
+M`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@
+M,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\
+MQL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``````
+M``!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``
+M``````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``
+M,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P
+M,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&
+MQF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```````
+M`'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```
+M`````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P
+M,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P
+M,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&
+M8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#``````````````````````````
+!`,;&
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.808.uu b/usr.sbin/pcvt/fonts/vt220l.808.uu
new file mode 100644
index 0000000..02ad944
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.808.uu
@@ -0,0 +1,49 @@
+begin 644 vt220l.808
+M``````````!^@:6!O9F!?G[_V__#Y_]^;/[^_GPX$``0.'S^?#@0`#A\./[^
+MUA`X$#A\_OY\$#@``!@\/!@``/__Y\/#Y___/&;#P\/#9CS_PYF]O9G#_Q\'
+M#7G-S,QX/&9F9CP8?A@X/#8R-'#PX']C?V-C9^;`&-L\Y^<\VQB`X/C^^."`
+M``(./OX^#@(`&#Q^&!A^/!AF9F9F9@!F`'_;VWL;&QL`?L/\9F8_PWX``'Y^
+M?@```!@\?AA^/!C_&#Q^&!@8&``8&!@8?CP8```8#/X,&````#!@_F`P````
+M`&!@8'X````D9O]F)````!`X?/[^````_OY\.!``````````````&#P\&!@`
+M&`!L;&P``````&QL_FS^;&P`,'S`?`;\,```QLP8,&;&`#AL.';<SGL`8&#`
+M```````8,&!@8#`8`#`8#`P,&#```&PX_CAL````,##\,#``````````,#!@
+M````_````````````!@8``8,&#!@P(``?,[>]N;&?``P<+`P,##\`'C,##A@
+MP/P`>,P,.`S,>``</&S,_@P>`/S`^`P,S'@`>,S`^,S,>`#\S`P8,#`P`'C,
+MS'C,S'@`>,S,?`S,>```,#```#`P```P,```,#!@&#!@P&`P&````'X``'X`
+M`#`8#`8,&#``/&8&#!@`&`!\QM[>WL!^`#!XS,S\S,P`_&9F?&9F_``\9L#`
+MP&8\`/AL9F9F;/@`_F)H>&AB_@#^8FAX:&#P`#QFP,#.9CX`S,S,_,S,S`!X
+M,#`P,#!X`!X,#`S,S'@`YFQX<'ALY@#P8&!@8F;^`,;N_M;&QL8`QN;VWL[&
+MQ@`X;,;&QFPX`/QF9GQ@8/``>,S,S-QX'`#\9F9\;&;F`'C,P'@,S'@`_+0P
+M,#`P>`#,S,S,S,QX`,S,S,S,>#``QL;&UO[^Q@#&QFPX.&S&`,S,S'@P,'@`
+M_,R8,&3,_`!X8&!@8&!X`,!@,!@,!@(`>!@8&!@8>``0.&S&````````````
+M``#_,#`8`````````'@,?,QV`.!@8'QF9OP```!XS,#,>``<#`Q\S,Q^````
+M>,S\P'@`.&Q@^&!@\````';,S'P,^.!@;'9F9N8`,`!P,#`P>``,`!P,#,S,
+M>.!@9FQX;.8`<#`P,#`P>````,S^_M;&````^,S,S,P```!XS,S,>````-QF
+M9GQ@\```=LS,?`P>``#<=F!@\````'S`?`;\`!`P?#`P-AP```#,S,S,=@``
+M`,;&QGPX````QM;^_FP```#&;#ALQ@```,S,S'P,^```_)@P9/P`'#`PX#`P
+M'``8&!@`&!@8`.`P,!PP,.``=MP````````0.&QLQL;^`'C,P,QX&`QX`,P`
+MS,S,?@`<`'C,_,!X`'[#/`8^9C\`S`!X#'S,?@#@`'@,?,Q^`#`P>`Q\S'X`
+M``!XP,!X##A^PSQF?F`\`,P`>,S\P'@`X`!XS/S`>`#,`'`P,#!X`'S&.!@8
+M&#P`X`!P,#`P>`#&.&S&_L;&`#`P`'C,_,P`'`#\8'A@_````'\,?\Q_`#YL
+MS/[,S,X`>,P`>,S,>```S`!XS,QX``#@`'C,S'@`>,P`S,S,?@``X`#,S,Q^
+M``#,`,S,?`SXPQ@\9F8\&`#,`,S,S,QX`!@8?L#`?A@8.&QD\&#F_`#,S'C\
+M,/PP,/C,S/K&S\;'#AL8/!@8V'`<`'@,?,Q^`#@`<#`P,'@``!P`>,S,>```
+M'`#,S,Q^``#X`/C,S,P`_`#,[/S<S``\;&P^`'X``#AL;#@`?```,``P8,#,
+M>`````#\P,```````/P,#```P\;,WC-FS`_#QLS;-V_/`Q@8`!@8&!@``#-F
+MS&8S````S&8S9LP``"*((H@BB"*(5:I5JE6J5:K;=]ONVW?;[A@8&!@8&!@8
+M&!@8&/@8&!@8&/@8^!@8&#8V-C;V-C8V`````/XV-C8``/@8^!@8&#8V]@;V
+M-C8V-C8V-C8V-C8``/X&]C8V-C8V]@;^````-C8V-OX````8&/@8^```````
+M``#X&!@8&!@8&!\````8&!@8_P````````#_&!@8&!@8&!\8&!@`````_P``
+M`!@8&!C_&!@8&!@?&!\8&!@V-C8V-S8V-C8V-S`_```````_,#<V-C8V-O<`
+M_P``````_P#W-C8V-C8W,#<V-C8``/\`_P```#8V]P#W-C8V&!C_`/\````V
+M-C8V_P``````_P#_&!@8`````/\V-C8V-C8V/P```!@8'Q@?```````?&!\8
+M&!@`````/S8V-C8V-C;_-C8V&!C_&/\8&!@8&!@8^``````````?&!@8____
+M______\`````______#P\/#P\/#P#P\/#P\/#P______````````=MS(W'8`
+M`'C,^,SXP,``_,S`P,#```#^;&QL;&P`_,Q@,&#,_````'[8V-AP``!F9F9F
+M?&#``';<&!@8&`#\,'C,S'@P_#ALQO[&;#@`.&S&QFQL[@`<,!A\S,QX````
+M?MO;?@``!@Q^V]M^8,`\8,#\P&`\`'C,S,S,S,P``/P`_`#\```P,/PP,`#\
+M`&`P&#!@`/P`&#!@,!@`_``.&QL8&!@8&!@8&!@8V-AP,#``_``P,```=MP`
+M=MP``#AL;#@`````````&!@`````````&`````\,#`SL;#P<>&QL;&P```!P
+7&#!@>```````/#P\/```````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.810.uu b/usr.sbin/pcvt/fonts/vt220l.810.uu
new file mode 100644
index 0000000..7cba2cb
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.810.uu
@@ -0,0 +1,60 @@
+begin 644 vt220l.810
+M`````````````'Z!I:6!@;V9@7Y^_]O;___#Y_]^;/[^?'PX.!`0````$#A\
+M_GPX$``X?'PX?/[^?#A\$!`X.'S^_GPX?````!@\/!@```#____GP\/G____
+M```\9D)"9CP``/__PYF]O9G#__\`#P</?<S,S'@``#QF9F8\&'X8`#\S/S`P
+M,'#PX`!_8W]C8V-GYL```-M^/.?G/'[;````@.#X_OC@@`````(./OX^#@(`
+M&!@\?A@8?CP8&&9F9F9F9@!F9@!_V]O;>QL;&QL`/&8P;,9L&,QX````````
+M?GY^?@`8&#Q^&!A^/!C_&!@\/'X8&!@8&!@8&!@8?CP\&!@`````&`S^#!@`
+M`````#!@_F`P``````#`P,#`_@``````)&;_9B0``!@8/#Q^?O__``#__WY^
+M/#P8&```````````````,'AX>#`P`#`P`&QL;"@```````!L;/YL;&S^;&P`
+M&'[8V'PV-OPP`,;,#!@X,&!FQ@`X;&PX,';<S'8`X.!@8,````````P8,#`P
+M,#`8#`!@,!@8&!@8,&````#&;#C^.&S&`````#`P_#`P````````<'`P,&``
+M``````#^```````````````P,```#`P8&#`P8&``?,;.WM;VYL9\`#!P,#`P
+M,#`P_`!\QL8&##A@QOX`?,;&!CP&QL9\``P,'#QLS/X,'@#^P,#\!@8&QGP`
+M'#!@P/S&QL9\`/[&!@P,&!@8&`!\QL;&?,;&QGP`?,;&QGX&#!AP`````#`P
+M```P,````#`P```P,&`````8,&#`8#`8``````#\``#\`````&`P&`P8,&``
+M?,;&!@P8&``8`'S&SMK6WL#&?``X?,;&QO[&QL8`_&9F9GQF9F;\`#QFP,#`
+MP,!F/`#X;&9F9F9F;/@`_F)@:'AH8&+^`/YB8&AX:&!@\``\9L#`P,[&9CP`
+MQL;&QO[&QL;&`#P8&!@8&!@8/``>#`P,#`S,S'@`YF9L;'AL;&;F`/!@8&!@
+M8&)F_@#&[O[^UL;&QL8`QN;F]M[.SL;&`#ALQL;&QL9L.`#\9F9F?&!@8/``
+M?,;&QL;&SGP.`/QF9F9X;&QFY@!\QL!@.`P&QGP`?EH8&!@8&!@\`,;&QL;&
+MQL;&?`!F9F9F9F9F/!@`QL;&QL;6_N[&`,;&;#@0.&S&Q@#,S,S,>#`P,'@`
+M_L:,&#!@PL;^`!X8&!@8&!@8'@``8&`P,!@8#`P`\#`P,#`P,##P`!`X;,8`
+M``````````````````#_,#`8`````````````'@,?,S,=@#@8&!\9F9F9MP`
+M````?,;`P,9\`!P,#'S,S,S,=@````!\QO[`P'P`.&Q@\&!@8&#P`````';,
+MS'P,S'C@8&!L=F9F9N8`,#``<#`P,#!X``P,``P,#`S,S'C@8&!F;'AX;.8`
+M<#`P,#`P,#!X`````,S^_M;6U@````#<YL;&QL8`````?,;&QL9\`````-QF
+M9F9\8/````!VS,S,?`P>````W'9F8&#P`````'S&<!S&?``@8&#\8&!@;#@`
+M````S,S,S,QV`````,;&QL9L.`````#&UM;^_FP`````QFPX.&S&`````,;&
+MQGX&!OP```#^C!@P8OX`#A@8&'`8&!@.`!@8&!@`&!@8&`#@,#`P'#`P,.``
+M=MP``````````````!`X;,;&_@!\QL#`P,9\#`8\`,P`S,S,S,QV```.`'S&
+M_L#`?``\9@`\!CYF9C\``,P`>`Q\S,QV``!P`'@,?,S,=@`P,`!X#'S,S'8`
+M````?L#`P'X&/#QF`#QF?F!@/`#,``!\QO[`P'P``'``?,;^P,!\``#,`'`P
+M,#`P>``X;``X&!@8&#P``.``<#`P,#!X`,8`.&S&_L;&Q@`P,`!XS/S,S,P`
+M'`#\8&!X8&#\`````'X;?MC8?P`^;,S,_LS,S,X`.&P`?,;&QL9\``#&`'S&
+MQL;&?```<`!\QL;&QGP`>,P`S,S,S,QV``!P`,S,S,S,=@``Q@#&QL9^!@;\
+MQCALQL;&QFPX`,8`QL;&QL;&?``8&'[`P,#`?A@8.&QD8/A@8.;\`,S,>##\
+M,/PP,`#XS,S,^L;/QL<`#AL8&#P8&!C8<``<`'@,?,S,=@``.`!P,#`P,'@`
+M`!P`?,;&QL9\```<`,S,S,S,=@!P_AP`W.;&QL8`_@#&YO;^WL[&`#QL;&P^
+M`'X````X;&QL.`!\````,``P,&#`QL9\``````#^P,``````````_@8&````
+MP\;,V#Y[PP8,#\/&S-DS9\T/`P,8&``8&#P\/!@`````,V;,9C,``````,QF
+M,V;,```BB"*((H@BB"*(5:I5JE6J5:I5JMMWV^[;=]ONVW<8&!@8&!@8&!@8
+M&!@8&!CX&!@8&!@8&/@8^!@8&!@V-C8V-O8V-C8V``````#^-C8V-@```/@8
+M^!@8&!@V-C;V!O8V-C8V-C8V-C8V-C8V-@```/X&]C8V-C8V-C;V!OX`````
+M-C8V-C;^`````!@8&/@8^````````````/@8&!@8&!@8&!@?`````!@8&!@8
+M_P```````````/\8&!@8&!@8&!@?&!@8&```````_P`````8&!@8&/\8&!@8
+M&!@8'Q@?&!@8&#8V-C8V-S8V-C8V-C8W,#\`````````/S`W-C8V-C8V-O<`
+M_P````````#_`/<V-C8V-C8V-S`W-C8V-@```/\`_P`````V-C;W`/<V-C8V
+M&!@8_P#_`````#8V-C8V_P````````#_`/\8&!@8``````#_-C8V-C8V-C8V
+M/P`````8&!@?&!\`````````'Q@?&!@8&```````/S8V-C8V-C8V-O\V-C8V
+M&!@8_QC_&!@8&!@8&!@8^````````````!\8&!@8_____________P``````
+M_______P\/#P\/#P\/#P#P\/#P\/#P\/#_______``````````!VW-C,W'8`
+M`'S&QMS&QMS`P/YF9&!@8&!@\`#^;&QL;&QL;.X`_L9@,!@P8,;^`````'_,
+MS,S,>`````!F9F9V;&#`````=MP8&!@8`#P8?MO;VWX8/``X;,;&_L;&;#@`
+M.&S&QL9L;&SN`!PR,!A\S,S,>`````!VV]O;;@```P9\S]O;\SY@P```'#!@
+M?&`P'````'S&QL;&QL8`````_@#^`/X`````,##\,#``_`!@,!@,&#!@`/X`
+M#!@P8#`8#`#^``X;&Q@8&!@8&!@8&!@8&!@8V-AP```P,`#\`#`P`````';<
+M`';<```X;&QL.````````````!@8````````````&``````/#`P,[&QL-#P<
+H>&QL;&QL`````'#8&'#`^``````````\/#P\`````````````````&QL
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.814.uu b/usr.sbin/pcvt/fonts/vt220l.814.uu
new file mode 100644
index 0000000..7d918bb
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt220l.814
+M`````````````````````'Z!I8&!O9F!?@``````?O_;___#Y_]^````````
+M;/[^_OY\.!`````````0.'S^?#@0````````&#P\Y^?G&!@\```````8/'[_
+M_WX8&#P``````````!@\/!@``````/______Y\/#Y_______`````#QF0D)F
+M/`````#_____PYF]O9G#_____P``'@X:,GC,S,QX```````\9F9F/!A^&!@`
+M`````#\S/S`P,'#PX```````?V-_8V-C9^?FP``````8&-L\YSS;&!@`````
+M`(#`X/C^^.#`@````````@8./OX^#@8"```````8/'X8&!A^/!@``````&9F
+M9F9F9@!F9@``````?]O;VWL;&QL;`````'S&8#ALQL9L.`S&?```````````
+M`/[^_@``````&#Q^&!@8?CP8?@`````8/'X8&!@8&!@``````!@8&!@8&'X\
+M&``````````8#/X,&````````````#!@_F`P`````````````,#`P/X`````
+M```````H;/YL*```````````$#@X?'S^_@````````#^_GQ\.#@0````````
+M```````````````````8/#P\&!@`&!@`````9F9F)```````````````;&S^
+M;&QL_FQL````&!A\QL+`?`:&QGP8&```````PL8,&#!FQ@``````.&QL.';<
+MS,QV`````#`P,&````````````````P8,#`P,#`8#```````,!@,#`P,#!@P
+M`````````&8\_SQF````````````&!A^&!@`````````````````&!@8,```
+M````````_@`````````````````````8&````````@8,&#!@P(````````!\
+MQL[>]N;&QGP``````!@X>!@8&!@8?@``````?,8&#!@P8,;^``````!\Q@8&
+M/`8&QGP```````P</&S,_@P,'@``````_L#`P/P&!L9\```````X8,#`_,;&
+MQGP``````/[&!@P8,#`P,```````?,;&QGS&QL9\``````!\QL;&?@8&#'@`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@``````!\QL8,&!@`&!@``````'S&
+MQM[>WMS`?```````$#ALQL;^QL;&``````#\9F9F?&9F9OP``````#QFPL#`
+MP,)F/```````^&QF9F9F9FSX``````#^9F)H>&AB9OX``````/YF8FAX:&!@
+M\```````/&;"P,#>QF8Z``````#&QL;&_L;&QL8``````#P8&!@8&!@8/```
+M````'@P,#`P,S,QX``````#F9FQL>&QL9N8``````/!@8&!@8&)F_@``````
+MQN[^_M;&QL;&``````#&YO;^WL[&QL8``````#ALQL;&QL9L.```````_&9F
+M9GQ@8&#P``````!\QL;&QM;>?`P.`````/QF9F9\;&9FY@``````?,;&8#@,
+MQL9\``````!^?EH8&!@8&#P``````,;&QL;&QL;&?```````QL;&QL;&;#@0
+M``````#&QL;&UM;^?&P``````,;&;#@X.&S&Q@``````9F9F9CP8&!@\````
+M``#^QHP8,&#"QOX``````#PP,#`P,#`P/```````@,#@<#@<#@8"```````\
+M#`P,#`P,##P````0.&S&`````````````````````````````/\`,#`8````
+M`````````````````'@,?,S,=@``````X&!@>&QF9F9\``````````!\QL#`
+MQGP``````!P,##QLS,S,=@``````````?,;^P,9\```````X;&1@\&!@8/``
+M`````````';,S,Q\#,QX````X&!@;'9F9F;F```````8&``X&!@8&#P`````
+M``8&``X&!@8&9F8\````X&!@9FQX;&;F```````X&!@8&!@8&#P`````````
+M`.S^UM;6Q@``````````W&9F9F9F``````````!\QL;&QGP``````````-QF
+M9F9\8&#P````````=LS,S'P,#!X```````#<=F9@8/```````````'S&<!S&
+M?```````$#`P_#`P,#8<``````````#,S,S,S'8``````````,;&QFPX$```
+M````````QL;6UOYL``````````#&;#@X;,8``````````,;&QL9^!@SX````
+M````_LP8,&;^```````.&!@8<!@8&`X``````!@8&!@`&!@8&```````<!@8
+M&`X8&!AP``````!VW```````````````````$#ALQL;^````````/&;"P,#"
+M9CP,!GP```#,S`#,S,S,S'8`````#!@P`'S&_L#&?``````0.&P`>`Q\S,QV
+M``````#,S`!X#'S,S'8`````8#`8`'@,?,S,=@`````X;#@`>`Q\S,QV````
+M`````#QF8&8\#`8\````$#AL`'S&_L#&?```````S,P`?,;^P,9\`````&`P
+M&`!\QO[`QGP``````&9F`#@8&!@8/``````8/&8`.!@8&!@\`````&`P&``X
+M&!@8&#P`````QL80.&S&QO[&Q@```#AL.``X;,;&_L;&````&#!@`/YF8'Q@
+M9OX`````````S'8V?MC8;@``````/FS,S/[,S,S.`````!`X;`!\QL;&QGP`
+M`````,;&`'S&QL;&?`````!@,!@`?,;&QL9\`````#!XS`#,S,S,S'8`````
+M8#`8`,S,S,S,=@``````QL8`QL;&QGX&#'@`QL8`.&S&QL;&;#@```#&Q@#&
+MQL;&QL;&?``````8&#QF8&!F/!@8`````#AL9&#P8&!@YOP``````&9F/!A^
+M&'X8&`````#XS,SXQ,S>S,S&``````X;&!@8?A@8&!C8<```&#!@`'@,?,S,
+M=@`````,&#``.!@8&!@\`````!@P8`!\QL;&QGP`````&#!@`,S,S,S,=@``
+M````=MP`W&9F9F9F````=MP`QN;V_M[.QL8`````/&QL/@!^```````````X
+M;&PX`'P````````````P,``P,&#&QGP```````````#^P,#`````````````
+M`/X&!@8``````,#`QLS8,&#<A@P8/@``P,#&S-@P9LZ>/@8&````&!@`&!@\
+M/#P8`````````#9LV&PV````````````V&PV;-@``````!%$$4011!%$$401
+M1!%$5:I5JE6J5:I5JE6J5:K==]UWW7?==]UWW7?==Q@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&/@8^!@8&!@8&#8V-C8V-C;V-C8V-C8V````
+M`````/XV-C8V-C8``````/@8^!@8&!@8&#8V-C8V]@;V-C8V-C8V-C8V-C8V
+M-C8V-C8V-C8``````/X&]C8V-C8V-C8V-C8V]@;^````````-C8V-C8V-OX`
+M```````8&!@8&/@8^`````````````````#X&!@8&!@8&!@8&!@8&!\`````
+M```8&!@8&!@8_P````````````````#_&!@8&!@8&!@8&!@8&!\8&!@8&!@`
+M````````_P```````!@8&!@8&!C_&!@8&!@8&!@8&!@?&!\8&!@8&!@V-C8V
+M-C8V-S8V-C8V-C8V-C8V-S`_```````````````_,#<V-C8V-C8V-C8V-O<`
+M_P``````````````_P#W-C8V-C8V-C8V-C8W,#<V-C8V-C8``````/\`_P``
+M`````#8V-C8V]P#W-C8V-C8V&!@8&!C_`/\````````V-C8V-C8V_P``````
+M````````_P#_&!@8&!@8`````````/\V-C8V-C8V-C8V-C8V/P```````!@8
+M&!@8'Q@?```````````````?&!\8&!@8&!@`````````/S8V-C8V-C8V-C8V
+M-C;_-C8V-C8V&!@8&!C_&/\8&!@8&!@8&!@8&!@8^``````````````````?
+M&!@8&!@8__________________\`````````__________#P\/#P\/#P\/#P
+M\/#P#P\/#P\/#P\/#P\/#P__________````````````````=MS8V-QV````
+M`````'S&_,;&_,#`0````/[&QL#`P,#`P`````````#^;&QL;&QL``````#^
+MQF`P&#!@QOX``````````'[8V-C8<`````````!F9F9F?&!@P````````';<
+M&!@8&!@``````'X8/&9F9CP8?@``````.&S&QO[&QFPX```````X;,;&QFQL
+M;.X``````!XP&`P^9F9F/```````````?MO;?@`````````#!G[;V_-^8,``
+M`````!XP8&!^8&`P'@```````'S&QL;&QL;&````````_@``_@``_@``````
+M```8&'X8&```_P``````,!@,!@P8,`!^```````,&#!@,!@,`'X```````X;
+M&Q@8&!@8&!@8&!@8&!@8&!@8V-AP````````&!@`?@`8&```````````=MP`
+M=MP````````X;&PX````````````````````&!@`````````````````&```
+M```````/#`P,#`SL;#P<`````-AL;&QL;```````````<-@P8,CX````````
+=``````!\?'Q\?'P`````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.816.uu b/usr.sbin/pcvt/fonts/vt220l.816.uu
new file mode 100644
index 0000000..62393e1
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.816.uu
@@ -0,0 +1,95 @@
+begin 644 vt220l.816
+M````````````````````````?H&E@8&]F8&!?@```````'[_V___P^?__WX`
+M`````````&S^_O[^?#@0```````````0.'S^?#@0```````````8/#SGY^<8
+M&#P`````````&#Q^__]^&!@\`````````````!@\/!@```````#________G
+MP\/G________```````\9D)"9CP``````/______PYF]O9G#______\``!X.
+M&C)XS,S,S'@````````\9F9F9CP8?A@8````````/S,_,#`P,'#PX```````
+M`']C?V-C8V-GY^;`````````&!C;/.<\VQ@8``````"`P.#P^/[X\.#`@```
+M`````@8.'C[^/AX.!@(````````8/'X8&!A^/!@`````````9F9F9F9F9@!F
+M9@```````'_;V]M[&QL;&QL``````'S&8#ALQL9L.`S&?```````````````
+M_O[^_@```````!@\?A@8&'X\&'X````````8/'X8&!@8&!@8````````&!@8
+M&!@8&'X\&````````````!@,_@P8```````````````P8/Y@,```````````
+M`````,#`P/X``````````````"AL_FPH`````````````!`X.'Q\_OX`````
+M``````#^_GQ\.#@0```````````````````````````````8/#P\&!@8`!@8
+M``````!F9F8D``````````````````!L;/YL;&S^;&P``````!@8?,;"P'P&
+MAL9\&!@```````#"Q@P8,&#&A@```````#AL;#AVW,S,S'8``````#`P,&``
+M````````````````#!@P,#`P,#`8#````````#`8#`P,#`P,&#``````````
+M``!F//\\9@``````````````&!A^&!@````````````````````8&!@P````
+M`````````'X````````````````````````8&````````````@8,&#!@P(``
+M``````!\QL;.WO;FQL9\````````&#AX&!@8&!@8?@```````'S&!@P8,&#`
+MQOX```````!\Q@8&/`8&!L9\````````#!P\;,S^#`P,'@```````/[`P,#\
+M!@8&QGP````````X8,#`_,;&QL9\````````_L8&!@P8,#`P,````````'S&
+MQL9\QL;&QGP```````!\QL;&?@8&!@QX```````````8&````!@8````````
+M````&!@````8&#``````````!@P8,&`P&`P&````````````?@``?@``````
+M``````!@,!@,!@P8,&````````!\QL8,&!@8`!@8````````?,;&QM[>WMS`
+M?````````!`X;,;&_L;&QL8```````#\9F9F?&9F9F;\````````/&;"P,#`
+MP,)F/````````/AL9F9F9F9F;/@```````#^9F)H>&A@8F;^````````_F9B
+M:'AH8&!@\````````#QFPL#`WL;&9CH```````#&QL;&_L;&QL;&````````
+M/!@8&!@8&!@8/````````!X,#`P,#,S,S'@```````#F9F9L>'AL9F;F````
+M````\&!@8&!@8&)F_@```````,;N_O[6QL;&QL8```````#&YO;^WL[&QL;&
+M````````?,;&QL;&QL;&?````````/QF9F9\8&!@8/````````!\QL;&QL;&
+MUMY\#`X`````_&9F9GQL9F9FY@```````'S&QF`X#`;&QGP```````!^?EH8
+M&!@8&!@\````````QL;&QL;&QL;&?````````,;&QL;&QL9L.!````````#&
+MQL;&UM;6_NYL````````QL9L?#@X?&S&Q@```````&9F9F8\&!@8&#P`````
+M``#^QH8,&#!@PL;^````````/#`P,#`P,#`P/`````````"`P.!P.!P.!@(`
+M```````\#`P,#`P,#`P\`````!`X;,8`````````````````````````````
+M````_P``,#`8````````````````````````>`Q\S,S,=@```````.!@8'AL
+M9F9F9GP```````````!\QL#`P,9\````````'`P,/&S,S,S,=@``````````
+M`'S&_L#`QGP````````X;&1@\&!@8&#P````````````=LS,S,S,?`S,>```
+M`.!@8&QV9F9F9N8````````8&``X&!@8&!@\````````!@8`#@8&!@8&!F9F
+M/````.!@8&9L>'AL9N8````````X&!@8&!@8&!@\````````````[/[6UM;6
+MQ@```````````-QF9F9F9F8```````````!\QL;&QL9\````````````W&9F
+M9F9F?&!@\````````';,S,S,S'P,#!X```````#<=F9@8&#P````````````
+M?,9@.`S&?````````!`P,/PP,#`P-AP```````````#,S,S,S,QV````````
+M````QL;&QFPX$````````````,;&UM;6_FP```````````#&;#@X.&S&````
+M````````QL;&QL;&?@8,^````````/[,&#!@QOX````````.&!@8<!@8&!@.
+M````````&!@8&``8&!@8&````````'`8&!@.&!@8&'````````!VW```````
+M```````````````0.&S&QL;^`````````#QFPL#`P,)F/`P&?`````#,``#,
+MS,S,S,QV```````,&#``?,;^P,#&?```````$#AL`'@,?,S,S'8```````#,
+M``!X#'S,S,QV``````!@,!@`>`Q\S,S,=@``````.&PX`'@,?,S,S'8`````
+M`````#QF8&!F/`P&/``````0.&P`?,;^P,#&?````````,8``'S&_L#`QGP`
+M`````&`P&`!\QO[`P,9\````````9@``.!@8&!@8/```````&#QF`#@8&!@8
+M&#P``````&`P&``X&!@8&!@\``````#&`!`X;,;&_L;&Q@`````X;#@`.&S&
+MQO[&QL8`````&#!@`/YF8'Q@8&;^``````````!L_K(R?MC8;@```````#YL
+MS,S^S,S,S,X``````!`X;`!\QL;&QL9\````````Q@``?,;&QL;&?```````
+M8#`8`'S&QL;&QGP``````#!XS`#,S,S,S,QV``````!@,!@`S,S,S,S,=@``
+M````QL8``,;&QL;&QGX&#'@`QL8`?,;&QL;&QL9\`````,;&`,;&QL;&QL;&
+M?```````&!@\9F!@8&8\&!@``````#AL9&#P8&!@8.;\````````9F8\&'X8
+M?A@8&```````^,S,^,3,WLS,S,8```````X;&!@8?A@8&!@8V'`````8,&``
+M>`Q\S,S,=@``````#!@P`#@8&!@8&#P``````!@P8`!\QL;&QL9\```````8
+M,&``S,S,S,S,=@```````';<`-QF9F9F9F8`````=MP`QN;V_M[.QL;&````
+M```\;&P^`'X`````````````.&QL.`!\```````````````P,``P,&#`QL9\
+M`````````````/[`P,#```````````````#^!@8&!@```````,#`PL;,&#!@
+MW(8,&#X```#`P,+&S!@P9LZ>/@8&`````!@8`!@8&#P\/!@````````````V
+M;-AL-@``````````````V&PV;-@````````11!%$$4011!%$$4011!%$5:I5
+MJE6J5:I5JE6J5:I5JMUWW7?==]UWW7?==]UWW7<8&!@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&!@8^!CX&!@8&!@8&!@V-C8V-C8V]C8V-C8V
+M-C8V`````````/XV-C8V-C8V-@``````^!CX&!@8&!@8&!@V-C8V-O8&]C8V
+M-C8V-C8V-C8V-C8V-C8V-C8V-C8V-@``````_@;V-C8V-C8V-C8V-C8V-O8&
+M_@``````````-C8V-C8V-OX``````````!@8&!@8^!CX````````````````
+M````^!@8&!@8&!@8&!@8&!@8&!\``````````!@8&!@8&!C_````````````
+M````````_Q@8&!@8&!@8&!@8&!@8&!\8&!@8&!@8&`````````#_````````
+M```8&!@8&!@8_Q@8&!@8&!@8&!@8&!@?&!\8&!@8&!@8&#8V-C8V-C8W-C8V
+M-C8V-C8V-C8V-C<P/P`````````````````_,#<V-C8V-C8V-C8V-C8V]P#_
+M`````````````````/\`]S8V-C8V-C8V-C8V-C8W,#<V-C8V-C8V-@``````
+M_P#_```````````V-C8V-O<`]S8V-C8V-C8V&!@8&!C_`/\``````````#8V
+M-C8V-C;_`````````````````/\`_Q@8&!@8&!@8`````````/\V-C8V-C8V
+M-C8V-C8V-C8_```````````8&!@8&!\8'P`````````````````?&!\8&!@8
+M&!@8&``````````_-C8V-C8V-C8V-C8V-C8V_S8V-C8V-C8V&!@8&!C_&/\8
+M&!@8&!@8&!@8&!@8&!CX````````````````````'Q@8&!@8&!@8________
+M_____________P````````#____________P\/#P\/#P\/#P\/#P\/#P#P\/
+M#P\/#P\/#P\/#P\/#_________\``````````````````';<V-C8W'8`````
+M``!XS,S,V,S&QL;,````````_L;&P,#`P,#`P```````````_FQL;&QL;&P`
+M````````_L9@,!@P8,;^````````````?MC8V-C8<```````````9F9F9F9\
+M8&#``````````';<&!@8&!@8`````````'X8/&9F9CP8?@`````````X;,;&
+M_L;&;#@````````X;,;&QFQL;&SN````````'C`8##YF9F9F/```````````
+M`'[;V]M^`````````````P9^V]OS?F#`````````'C!@8'Y@8&`P'@``````
+M``!\QL;&QL;&QL8``````````/X``/X``/X````````````8&'X8&```_P``
+M```````P&`P&#!@P`'X`````````#!@P8#`8#`!^````````#AL;&Q@8&!@8
+M&!@8&!@8&!@8&!@8&-C8V'```````````!@8`'X`&!@`````````````=MP`
+M=MP`````````.&QL.````````````````````````!@8````````````````
+M````&```````````#PP,#`P,[&QL/!P``````-AL;&QL;`````````````!P
+MV#!@R/@`````````````````?'Q\?'Q\?```````````````````````````
+!`#!@
+`
+end
diff --git a/usr.sbin/pcvt/ispcvt/Makefile b/usr.sbin/pcvt/ispcvt/Makefile
new file mode 100644
index 0000000..994b406
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= ispcvt
+MAN= ispcvt.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/ispcvt/ispcvt.8 b/usr.sbin/pcvt/ispcvt/ispcvt.8
new file mode 100644
index 0000000..b9eee0a
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.8
@@ -0,0 +1,99 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this 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.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 16:31:54 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt ISPCVT 8
+.Os
+.Sh NAME
+.Nm ispcvt
+.Nd verify if current video driver is pcvt driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl c
+.Op Fl d Ar device
+.Op Fl n
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the user to check whether the current video driver compiled
+into the kernel is a pcvt driver.
+The major and minor release numbers of
+the driver are also checked.
+Furthermore
+.Nm
+is also able to print out the values of all the
+.Dq Ar PCVT_XXXXXX
+compile time options, the driver in the current running kernel was
+compiled with.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Specifies a device for which the check is done.
+.It Fl v
+Specifies being verbose.
+On success the name and revision is reported, on
+failure which comparison failed.
+.It Fl n
+print the number of compiled-in virtual terminals.
+.It Fl c
+This options prints out the values of all
+.Dq Ar PCVT_XXXXXX
+#defines which were given to the compiler at the time the currently running
+kernel was compiled.
+Specifying
+.Fl v
+with the
+.Fl c
+option gives a verbose listing of the compile-time options.
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width indent -compact
+.It 0
+driver is pcvt and major and minor numbers match
+.It 1
+.Xr open 2
+or
+.Xr ioctl 2
+system call failed
+.It 2
+driver name mismatch
+.It 3
+name matched, release major number mismatch
+.It 4
+name and major number matched, release minor number mismatch
+.It 5
+usage error
+.El
+.Sh SEE ALSO
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/ispcvt/ispcvt.c b/usr.sbin/pcvt/ispcvt/ispcvt.c
new file mode 100644
index 0000000..40979ca
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list 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.
+ *
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * ispcvt - check for pcvt driver running and its options
+ * ------------------------------------------------------
+ *
+ * Last Edit-Date: [Fri Mar 31 10:24:43 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ struct pcvtid pcvtid;
+ struct pcvtinfo pcvtinfo;
+ int c;
+ char *p;
+ int verbose = 0;
+ int config = 0;
+ int dflag = 0;
+ int n_screens = 0;
+ int fd;
+ char *device;
+
+ while( (c = getopt(argc, argv, "cd:nv")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ config = 1;
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'n':
+ n_screens = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(dflag)
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ {
+ if(verbose)
+ warn("ERROR opening %s", device);
+ exit(1);
+ }
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(ioctl(fd, VGAPCVTID, &pcvtid) == -1)
+ {
+ if(verbose)
+ warn("ioctl VGAPCVTID failed, error");
+ exit(1);
+ }
+
+ if(!strcmp(pcvtid.name, PCVTIDNAME))
+ {
+ if(pcvtid.rmajor == PCVTIDMAJOR)
+ {
+ if(pcvtid.rminor != PCVTIDMINOR)
+ {
+ if(verbose)
+ warnx("minor revision: expected %d, got %d", PCVTIDMINOR, pcvtid.rminor);
+ exit(4); /* minor revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ warnx("major revision: expected %d, got %d", PCVTIDMAJOR, pcvtid.rmajor);
+ exit(3); /* major revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ warnx("name check: expected %s, got %s", PCVTIDNAME, pcvtid.name);
+ exit(2); /* name mismatch */
+ }
+
+ if(verbose)
+ {
+ warnx("\nkernel and utils match, driver name [%s], release [%1.1d.%02.2d]\n",
+ pcvtid.name, pcvtid.rmajor, pcvtid.rminor);
+ }
+
+ if(config == 0 && n_screens == 0)
+ exit(0);
+
+ if(ioctl(fd, VGAPCVTINFO, &pcvtinfo) == -1)
+ {
+ if(verbose)
+ warn("ioctl VGAPCVTINFO failed, error");
+ exit(1);
+ }
+
+ if(n_screens)
+ {
+ printf("%d", pcvtinfo.nscreens);
+ exit(0);
+ }
+
+ if(verbose)
+ {
+ fprintf(stderr,"PCVT_NSCREENS = %u\t\t", pcvtinfo.nscreens);
+ fprintf(stderr,"PCVT_UPDATEFAST = %u\n", pcvtinfo.updatefast);
+ fprintf(stderr,"PCVT_UPDATESLOW = %u\t\t", pcvtinfo.updateslow);
+ fprintf(stderr,"PCVT_SYSBEEPF = %u\n", pcvtinfo.sysbeepf);
+ fprintf(stderr,"PCVT_PCBURST = %u\t\t", pcvtinfo.pcburst);
+ fprintf(stderr,"PCVT_KBD_FIFO_SZ = %u\n\n", pcvtinfo.kbd_fifo_sz);
+
+ /* config booleans */
+
+ fprintf(stderr,"PCVT_132GENERIC = %s",
+ (pcvtinfo.compile_opts & CONF_132GENERIC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_24LINESDEF = %s",
+ (pcvtinfo.compile_opts & CONF_24LINESDEF) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_CTRL_ALT_DEL = %s",
+ (pcvtinfo.compile_opts & CONF_CTRL_ALT_DEL) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_INHIBIT_NUMLOCK = %s",
+ (pcvtinfo.compile_opts & CONF_INHIBIT_NUMLOCK) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_META_ESC = %s",
+ (pcvtinfo.compile_opts & CONF_META_ESC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NO_LED_UPDATE = %s",
+ (pcvtinfo.compile_opts & CONF_NO_LED_UPDATE) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NULLCHARS = %s",
+ (pcvtinfo.compile_opts & CONF_NULLCHARS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_PRETTYSCRNS = %s",
+ (pcvtinfo.compile_opts & CONF_PRETTYSCRNS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SCREENSAVER = %s",
+ (pcvtinfo.compile_opts & CONF_SCREENSAVER) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SETCOLOR = %s",
+ (pcvtinfo.compile_opts & CONF_SETCOLOR) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SHOWKEYS = %s",
+ (pcvtinfo.compile_opts & CONF_SHOWKEYS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SLOW_INTERRUPT = %s",
+ (pcvtinfo.compile_opts & CONF_SLOW_INTERRUPT) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_USEKBDSEC = %s",
+ (pcvtinfo.compile_opts & CONF_USEKBDSEC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_VT220KEYB = %s",
+ ((u_int)pcvtinfo.compile_opts & (u_int)CONF_VT220KEYB) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"XSERVER = %s",
+ (pcvtinfo.compile_opts & CONF_XSERVER) ? "ON" : "OFF");
+
+ next();
+ fprintf(stderr,"PCVT_GREENSAVER = %s",
+ (pcvtinfo.compile_opts & CONF_GREENSAVER) ? "ON" : "OFF");
+
+ fprintf(stderr,"\n\n");
+ }
+ else /* !verbose */
+ {
+ fprintf(stderr,"PCVT_NSCREENS = %u\n", pcvtinfo.nscreens);
+ fprintf(stderr,"PCVT_UPDATEFAST = %u\n", pcvtinfo.updatefast);
+ fprintf(stderr,"PCVT_UPDATESLOW = %u\n", pcvtinfo.updateslow);
+ fprintf(stderr,"PCVT_SYSBEEPF = %u\n", pcvtinfo.sysbeepf);
+ fprintf(stderr,"Compile options = 0x%08X\n", pcvtinfo.compile_opts);
+ }
+}
+
+usage()
+{
+ fprintf(stderr,"\nispcvt - verify current video driver is the pcvt-driver\n");
+ fprintf(stderr," usage: ispcvt [-c] [-d device] [-n] [-v]\n");
+ fprintf(stderr,"options: -c print compile time configuration\n");
+ fprintf(stderr," -d <name> use devicefile <name>\n");
+ fprintf(stderr," -n print number of virtual screens (to stdout)\n");
+ fprintf(stderr," -v be verbose\n\n");
+ exit(5);
+}
+
+next()
+{
+ static int i = 0;
+
+ fprintf(stderr, "%s", (i == 0) ? "\t\t" : "\n");
+
+ i = ~i;
+}
+
+/* EOF */
diff --git a/usr.sbin/pcvt/kbdio/Makefile b/usr.sbin/pcvt/kbdio/Makefile
new file mode 100644
index 0000000..1bbbdda
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+PROG= kbdio
+NOMAN= #true
+SRCS= kbdio.y lex.l y.tab.h
+
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR} #-g
+
+#YACC= bison
+#YFLAGS+= -yd # Bison only
+
+YFLAGS+= -v # verbose
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "kbdio is not installed automatically ...."
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/kbdio/kbdio.y b/usr.sbin/pcvt/kbdio/kbdio.y
new file mode 100644
index 0000000..9207d26
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/kbdio.y
@@ -0,0 +1,330 @@
+/* Hello emacs, this should be edited in -*- Fundamental -*- mode */
+%{
+/*
+ * Copyright (c) 1994 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.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: kbdio.y,v $
+ * Revision 1.2 1994/09/18 19:49:22 j
+ * Refined expr handling; can now set/clear bits.
+ *
+ * Revision 1.1 1994/09/18 12:57:13 j
+ * Initial revision
+ *
+ *
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/fcntl.h>
+#include <machine/cpufunc.h>
+#include <machine/pcvt_ioctl.h>
+
+#ifdef __NetBSD__
+#include <machine/pio.h>
+#endif
+
+#define KBD_DELAY \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); }
+
+#define YYDEBUG 1
+
+void yyerror(const char *msg);
+
+static void help(int), status(void), data(int), kbd(int), cmdbyte(int),
+ kbc(int), whatMCA(void);
+static int kbget(void);
+%}
+
+%union {
+ int num;
+}
+
+%token NEWLINE
+%token ALL CMD DATA DEFAULTS ECHOC ENABLE EXPR HELP ID LED
+%token MAKE ONLY RELEASE RESEND RESET SCAN STATUS TYPEMATIC
+%token WHAT
+%token <num> NUM
+
+%type <num> expr opr
+
+%%
+
+interpret: lines ;
+
+lines: line
+ | lines line
+ ;
+
+line: statements NEWLINE
+ | NEWLINE
+ | error NEWLINE { fprintf(stderr, "bing!\n"); }
+ ;
+
+statements: statement
+ | statements ';' statement
+ ;
+
+statement: '?' { help(0); }
+ | HELP { help(0); }
+ | HELP EXPR { help(1); }
+ | STATUS '?' { status(); }
+ | WHAT '?' { whatMCA(); }
+ | DATA '?' { data(kbget()); }
+ | LED '=' NUM { kbd(0xed); kbd($3); }
+ | ECHOC { kbd(0xee); kbget(); }
+ | SCAN '=' NUM { kbd(0xf0); kbd($3);
+ if($3 == 0) data(kbget()); }
+ | SCAN '?' { kbd(0xf0); kbd(0); data(kbget()); }
+ | ID '?' { kbd(0xf2); data(kbget());
+ data(kbget()); }
+ | TYPEMATIC '=' NUM ',' NUM
+ { kbd(0xf3);
+ if($3 > 1000) $3 = 1000;
+ if($5 > 30) $5 = 30;
+ if($5 < 2) $5 = 2;
+ kbd(
+ (int)
+ (8.0 * log(30.0 / (double)$5)
+ / log(2))
+ | ((($3 / 250) - 1) * 32)
+ );
+ }
+ | ENABLE { kbd(0xf4); }
+ | DEFAULTS { kbd(0xf6); }
+ | ALL TYPEMATIC { kbd(0xf7); }
+ | ALL MAKE RELEASE { kbd(0xf8); }
+ | ALL MAKE ONLY { kbd(0xf9); }
+ | ALL TYPEMATIC MAKE RELEASE
+ { kbd(0xfa); }
+ | NUM TYPEMATIC { kbd(0xfb); kbd($1); }
+ | NUM MAKE RELEASE { kbd(0xfc); kbd($1); }
+ | NUM MAKE ONLY { kbd(0xfd); kbd($1); }
+ | RESEND { kbd(0xfe); }
+ | RESET { kbd(0xff); }
+ | CMD '?' { kbc(0x20); cmdbyte(kbget()); }
+ | CMD '=' expr { kbc(0x60); kbd($3); }
+ | /* lambda */
+ ;
+
+expr: opr { $$ = $1; }
+ | expr '+' opr { $$ = $1 | $3; }
+ | expr '-' opr { $$ = $1 & ~($3); }
+ ;
+
+opr: NUM { $$ = $1; }
+ | CMD { kbc(0x20); $$ = kbget(); }
+ ;
+
+%%
+
+static void
+help(int topic) {
+ switch(topic) {
+ case 0:
+ printf(
+ "Input consists of lines, containing one or more semicolon-separated\n"
+ "statements. Numbers are implicitly hexadecimal, append a dot for\n"
+ "decimal numbers. Valid statements include:\n"
+ "help [expr]; give help [to expression syntax]\n"
+ "status ? interpret kbd ctrl status byte\n"
+ "what ? check for MCA type 1 or 2 motherboard controller\n"
+ "data ? get one byte of data\n"
+ "led = NUM set kbd LEDs\n"
+ "echo = NUM echo byte to kbd\n"
+ "scan = NUM; scan ? set scan code set; return current set\n"
+ "id ? get two id bytes\n"
+ "typematic=delay,rate set typematic delay(ms)&rate(1/s)\n"
+ "enable; defaults enable kbd; back to defaults\n"
+ "all typematic make all keys typematic\n"
+ "all make release make all keys sending make/release\n"
+ "all make only make all keys sending make only\n"
+ "all typematic make release make all keys typematic & make/release\n"
+ "NUM typematic make specific key typematic\n"
+ "NUM make release make specific key sending make/release\n"
+ "NUM make only make specific key sending make only\n"
+ "resend; reset resend last byte from kbd; reset kbd\n"
+ "cmd ? fetch kbd ctrl command byte\n"
+ "cmd = expr set kbd ctrl command byte\n"
+ "\n");
+ break;
+
+ case 1:
+ printf(
+ "Expressions can either consist of a number, possibly followed\n"
+ "by a + or - sign and bit values in numeric or symbolic form.\n"
+ "Symbolic bit values are:\n"
+ "SCCONV IGNPAR CLKLOW OVRINH TEST IRQ\n"
+ "\n");
+ break;
+ }
+}
+
+static void
+status(void) {
+ int c = inb(0x64);
+ if(c&0x80) printf("parity error | ");
+ if(c&0x40) printf("rx timeout | ");
+ if(c&0x20) printf("tx timeout | ");
+ if(c&0x10) printf("kbd released ");
+ else printf("kbd locked ");
+ if(c&0x08) printf("| cmd last sent ");
+ else printf("| data last sent ");
+ if(c&0x04) printf("| power-on ");
+ else printf("| test ok ");
+ if(c&0x02) printf("| ctrl write busy ");
+ else printf("| ctrl write ok ");
+ if(c&0x01) printf("| ctrl read ok\n");
+ else printf("| ctrl read empty\n");
+}
+
+/* see: Frank van Gilluwe, "The Undocumented PC", Addison Wesley 1994, pp 273 */
+
+static void
+whatMCA(void) {
+ int new, sav;
+ kbc(0x20); /* get command byte */
+ sav = kbget(); /* sav = command byte */
+ kbc(0x60); /* set command byte */
+ kbd(sav | 0x40); /* set keyboard xlate bit */
+ kbc(0x20); /* get keyboard command */
+ new = kbget(); /* new = command byte */
+ kbc(0x60); /* set command byte */
+ kbd(sav); /* restore command byte */
+ if(new & 0xbf)
+ printf("Hmm - looks like MCA type 1 motherboard controller\n");
+ else
+ printf("Hmm - looks like MCA type 2 motherboard controller\n");
+}
+
+static void
+kbd(int d) {
+ int i = 100000;
+ while(i && (inb(0x64) & 2)) i--;
+ if(i == 0) { printf("kbd write: timed out\n"); return; }
+ outb(0x60, d);
+}
+
+static void
+kbc(int d) {
+ int i = 100000;
+ while(i && (inb(0x64) & 2)) i--;
+ if(i == 0) { printf("ctrl write: timed out\n"); return; }
+ outb(0x64, d);
+}
+
+static int
+kbget(void) {
+ int i, c;
+ for(;;) {
+ i = 10000;
+ while(i && (inb(0x64) & 1) == 0) i--;
+ if(i == 0) { printf("data read: timed out\n"); return -1; }
+ KBD_DELAY
+ c = (unsigned char)inb(0x60);
+ switch(c) {
+ case 0: case 0xff:
+ printf("got kbd overrun\n"); break;
+ case 0xaa:
+ printf("got self-test OK\n"); break;
+ case 0xee:
+ printf("got ECHO byte\n"); break;
+ case 0xfa:
+ printf("got ACK\n"); break;
+ case 0xfc:
+ printf("got self-test FAIL\n"); break;
+ case 0xfd:
+ printf("got internal failure\n"); break;
+ case 0xfe:
+ printf("got RESEND request\n"); break;
+ default:
+ goto done;
+ }
+ }
+done:
+ return c;
+}
+
+static void
+cmdbyte(int d) {
+ if(d&0x40) printf("scan conv ");
+ else printf("pass thru ");
+ if(d&0x20) printf("| ign parity ");
+ else printf("| check parity ");
+ if(d&0x10) printf("| kbd clk low ");
+ else printf("| enable kbd ");
+ if(d&0x08) printf("| override kbd inh ");
+ if(d&0x04) printf("| test ok ");
+ else printf("| power-on ");
+ if(d&0x01) printf("| irq 1 enable\n");
+ else printf("| no irq\n");
+}
+
+static void
+data(int d) {
+ if(d < 0) return;
+ printf("data: 0x%02x\n", d);
+}
+
+void yyerror(const char *msg) {
+ fprintf(stderr, "yyerror: %s\n", msg);
+}
+
+int main(int argc, char **argv) {
+ int fd;
+
+ if(argc > 1) yydebug = 1;
+
+ if((fd = open("/dev/console", O_RDONLY)) < 0)
+ fd = 0;
+
+ if(ioctl(fd, KDENABIO, 0) < 0) {
+ perror("ioctl(KDENABIO)");
+ return 1;
+ }
+ yyparse();
+
+ (void)ioctl(fd, KDDISABIO, 0);
+ return 0;
+}
+
diff --git a/usr.sbin/pcvt/kbdio/lex.l b/usr.sbin/pcvt/kbdio/lex.l
new file mode 100644
index 0000000..de1a252
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/lex.l
@@ -0,0 +1,98 @@
+%{
+/*
+ * Copyright (c) 1994 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.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: lex.l,v $
+ * Revision 1.2 1994/09/18 19:48:45 j
+ * Added the symbolic values for kbd cmd byte.
+ *
+ * Revision 1.1 1994/09/18 12:57:13 j
+ * Initial revision
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "y.tab.h"
+
+extern YYSTYPE yylval;
+
+%}
+
+D [0-9a-fA-F]
+
+%%
+
+all { return ALL; }
+cmd { return CMD; }
+data { return DATA; }
+defaults { return DEFAULTS; }
+echo { return ECHOC; }
+enable { return ENABLE; }
+expr { return EXPR; }
+help { return HELP; }
+id { return ID; }
+led { return LED; }
+make { return MAKE; }
+only { return ONLY; }
+release { return RELEASE; }
+resend { return RESEND; }
+reset { return RESET; }
+scan { return SCAN; }
+status { return STATUS; }
+typematic { return TYPEMATIC; }
+what { return WHAT; }
+
+ /* numeric values */
+clklow { yylval.num = 0x10; return NUM; }
+ignpar { yylval.num = 0x20; return NUM; }
+irq { yylval.num = 0x01; return NUM; }
+ovrinh { yylval.num = 0x08; return NUM; }
+scconv { yylval.num = 0x40; return NUM; }
+test { yylval.num = 0x04; return NUM; }
+
+{D}({D}*)\. { sscanf(yytext, "%d", &yylval.num); return NUM; }
+
+{D}({D}*) { sscanf(yytext, "%x", &yylval.num); return NUM; }
+
+[ \t] { /* ignore */ }
+
+\n { return NEWLINE; }
+
+. { return yytext[0]; }
diff --git a/usr.sbin/pcvt/kcon/Makefile b/usr.sbin/pcvt/kcon/Makefile
new file mode 100644
index 0000000..564cd84
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= kcon
+DEVICE= /dev/ttyv0
+
+CFLAGS+= -I${.CURDIR}/../keycap -DKEYB_DEVICE=\"${DEVICE}\"
+
+DPADD= ${LIBKEYCAP}
+LDADD= -lkeycap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/kcon/kcon.1 b/usr.sbin/pcvt/kcon/kcon.1
new file mode 100644
index 0000000..075937b
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.1
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this 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.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 17:04:14 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt KCON 1
+.Os
+.Sh NAME
+.Nm kcon
+.Nd pcvt keyboard control and remapping
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar delay
+.Op Fl l
+.Op Fl m Ar map
+.Op Fl o
+.Op Fl p
+.Op Fl R
+.Op Fl r Ar rate
+.Op Fl s
+.Op Fl t Ns Ar +/-
+.Op Fl x
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is used for controlling all aspects of keyboard configuration for the 'pcvt'
+video driver.
+.Pp
+The available options are:
+.Bl -tag -width flag
+.It Fl d Ar delay
+Specifies the delay after which the last key entered will be repeated by the
+Keyboard.
+Valid values are 0..3 corresponding to delays of 250, 500, 750 and
+1000 milliseconds.
+.It Fl l
+Displays the current keyboard map in use by the driver.
+.It Fl m Ar map
+Specifies the map entry to be searched in the keyboard capabilities database
+.Nm keycap .
+The database is searched for the entry and if found, the mapping
+is loaded and is used in the driver from then on.
+.It Fl o
+Switches display of control codes to octal in the listing of the current map.
+To be used in conjunction with the
+.Fl l
+option.
+.It Fl p
+Uses 'pure' output when listing - the Escape character is displayed in either
+octal or hexadecimal and not as 'ESC'. To be used in conjunction with the
+.Fl l
+option.
+.It Fl r Ar rate
+Specifies the character repetition rate.
+Valid argument values are 0...31
+corresponding to rates of 30 characters/second ... 2 characters/second.
+.It Fl R
+Reset the Keyboard.
+.It Fl s
+Displays the current settings of the rate and delay values.
+.It Fl t Ar +/-
+Specify this option to enable
+.Pq Ar +
+or disable
+.Pq Ar -
+the repetition of keys.
+.It Fl x
+Switches display of control codes to hexadecimal in the listing of the current map.
+To be used in conjunction with the
+.Fl l
+option.
+This is the default behaviour.
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+Keyboard capabilities data base file if nothing else was chosen during installation.
+.It Pa /dev/console
+Keyboard raw device.
+.El
+.Sh SEE ALSO
+.Xr keycap 3 ,
+.Xr keycap 5
+.Sh BUGS
+The
+.Nm
+utility detects several inconsistencies in the keycap database.
+In case of errors
+.Nm
+exits with an error message.
+If this happens, the keyboard may remain in
+an undefined state.
+To recover from such situation, execute
+.Dq Li kcon -m default .
+.Sh EXAMPLES
+The command
+.Dq Li kcon -m gb
+loads the entry 'gb' from the keycap file into the keyboard to switch to
+a british keyboard behaviour.
+.Pp
+The command
+.Dq Li kcon -r 0 -d 0
+switches the keyboard to emit characters at a rate of 30 characters per second
+after a key has been held down for 250 milliseconds.
diff --git a/usr.sbin/pcvt/kcon/kcon.c b/usr.sbin/pcvt/kcon/kcon.c
new file mode 100644
index 0000000..4318bf2
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.c
@@ -0,0 +1,743 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1993 Holger Veit.
+ *
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to 386BSD by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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
+ * Hellmuth Michaelis and Holger Veit
+ * 4. The name 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 ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * kcon.c Keyboard control and remapping
+ * ----------------------------------------------
+ *
+ * based on "keymap" which was written by
+ * Holger Veit (veit@du9ds3.uni-duisburg.de)
+ *
+ * Last Edit-Date: [Mon Mar 27 17:03:50 2000]";
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <machine/pcvt_ioctl.h>
+
+#include "keycap.h"
+
+int Rf = 0;
+int df = 0;
+int lf = 0;
+int mf = 0;
+int of = 0;
+int pf = 0;
+int rf = 0;
+int tf = 0;
+int xf = 0;
+int sf = 0;
+
+/*---------------------------------------------------------------------------*
+ * main entry
+ *---------------------------------------------------------------------------*/
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c = 0;
+
+ int errf = 0;
+
+ int rate = -1;
+ int delay = -1;
+ char *map;
+ int kbfd;
+
+ while((c = getopt(argc, argv, "Rd:lm:opr:st:x")) != -1)
+ {
+ switch(c)
+ {
+ case 'R':
+ Rf = 1;
+ break;
+
+ case 'd':
+ df = 1;
+ delay = atoi(optarg);
+ break;
+
+ case 'l':
+ lf = 1;
+ break;
+
+ case 'm':
+ mf = 1;
+ map = optarg;
+ break;
+
+ case 'o':
+ if(xf)
+ errf = 1;
+ else
+ of = 1;
+ break;
+
+ case 'p':
+ pf = 1;
+ break;
+
+ case 'r':
+ rf = 1;
+ rate = atoi(optarg);
+ break;
+
+ case 's':
+ sf = 1;
+ break;
+
+ case 't':
+ if(*optarg == '+')
+ tf = 1;
+ else if(*optarg == '-')
+ tf = -1;
+ else
+ errf = 1;
+ break;
+
+ case 'x':
+ if(of)
+ errf = 1;
+ else
+ xf = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if((Rf == 0 && df == 0 && lf == 0 && tf == 0 && sf == 0 &&
+ rf == 0 && mf == 0 ) || errf)
+ {
+ usage();
+ }
+
+ if((kbfd = open(KEYB_DEVICE, 0)) < 0)
+ {
+ perror("kcon: keyboard open failiure");
+ exit(1);
+ }
+
+ if(sf)
+ {
+ showtypeamatic(kbfd);
+ exit(0);
+ }
+
+ if(lf)
+ {
+ listcurrent(kbfd);
+ exit(0);
+ }
+
+ if (Rf)
+ {
+ if (ioctl(kbfd, KBDRESET, 0) < 0) {
+ perror ("kcon: ioctl KBDRESET failed");
+ exit (1);
+ }
+ }
+
+ if(tf)
+ {
+ setrepeat(kbfd, tf);
+ }
+
+ if(df || rf)
+ {
+ if(delay > 3)
+ {
+ fprintf(stderr,"Delay value (%d) out of range, possible values are 0..3!\n",delay);
+ exit(1);
+ }
+ if(rate > 31)
+ {
+ fprintf(stderr,"Rate value (%d) out of range, possible values are 0..31!\n",rate);
+ exit(1);
+ }
+ settypeam(kbfd, delay, rate);
+ }
+
+ if(mf)
+ {
+ remapkeys(kbfd, map);
+ }
+
+ close(kbfd);
+ exit(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * display usage info & exit
+ *---------------------------------------------------------------------------*/
+usage()
+{
+ fprintf(stderr, "\nkcon: keyboard control and remapping utility for pcvt video driver\n");
+ fprintf(stderr, "usage: [-R] [-d delay] [-l] [-m map] [-o] [-p] [-r rate] [-t +/-] [-x]\n");
+ fprintf(stderr, " -R full reset of keyboard\n");
+ fprintf(stderr, " -d delay until a key is repeated (range: 0...3 => 250...1000ms)\n");
+ fprintf(stderr, " -l produce listing of current keyboard mapping\n");
+ fprintf(stderr, " -m set keyboard remapping from a keycap entry\n");
+ fprintf(stderr, " -o set octal output for listing\n");
+ fprintf(stderr, " -p pure, don't display escape as 'ESC' for listing\n");
+ fprintf(stderr, " -r chars/second repeat value (range: 0...31 => 30...2 chars/sec)\n");
+ fprintf(stderr, " -s show, display the current keyboard typematic values\n");
+ fprintf(stderr, " -t switch repeat on(+) or off(-)\n");
+ fprintf(stderr, " -x set hexadecimal output for listing\n\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * convert control char in string to printable values
+ *---------------------------------------------------------------------------*/
+char *showcntrl(s)
+u_char *s;
+{
+ static char res_str[80];
+ static char conv_buf[80];
+ int i;
+
+ res_str[0] = '\0';
+
+ for(i = 0; s[i]; i++)
+ {
+ if(((s[i] > 0x20) && (s[i] <= 0x7e)) || ((s[i] >= 0xa0) && (s[i] <= 0xff)))
+ {
+ conv_buf[0] = s[i];
+ conv_buf[1] = '\0';
+ }
+ else if((s[i] == 0x1b) && (pf == 0))
+ {
+ strcpy(conv_buf,"ESC ");
+ }
+ else if(of)
+ {
+ sprintf(conv_buf,"\\%03.3o ", s[i]);
+ }
+ else
+ {
+ sprintf(conv_buf,"0x%02.2X ", s[i]);
+ }
+ strcat(res_str, conv_buf);
+ }
+ return(res_str);
+}
+
+/*---------------------------------------------------------------------------*
+ * list the current keyboard mapping
+ *---------------------------------------------------------------------------*/
+listcurrent(kbfd)
+int kbfd;
+{
+ static char *keytypetab[] = {
+ "NONE ",
+ "SHIFT ",
+ "ALT/META ",
+ "NUMLOCK ",
+ "CONTROL ",
+ "CAPSLOCK ",
+ "ASCII ",
+ "SCROLL ",
+ "FUNCTION ",
+ "KEYPAD ",
+ "BREAK ",
+ "ALTGR ",
+ "SHIFTLOCK",
+ "CURSOR ",
+ "RETURN "
+ };
+
+ struct kbd_ovlkey keyboardmap[KBDMAXKEYS];
+ struct kbd_ovlkey *kbmapp;
+ int keytype;
+ int altgr_defined;
+ int i;
+
+ altgr_defined = 0;
+ kbmapp = keyboardmap;
+
+ for (i = 0; i < KBDMAXKEYS; i++)
+ {
+ kbmapp->keynum = i;
+
+ if(ioctl(kbfd, KBDGCKEY, kbmapp) < 0)
+ {
+ perror("kcon: ioctl KBDGCKEY failed");
+ exit(1);
+ }
+
+ if((kbmapp->type & KBD_MASK) == KBD_ALTGR)
+ altgr_defined = i;
+
+ kbmapp++;
+ }
+
+ if(altgr_defined)
+ {
+ printf("S Key KeyType Normal Shift Control Altgr \n");
+ printf("- --- --------- --------------- --------------- --------------- ---------------\n");
+ }
+ else
+ {
+ printf("S Key KeyType Normal Shift Control \n");
+ printf("- --- --------- --------------- --------------- ---------------\n");
+ }
+
+ kbmapp = &keyboardmap[1];
+
+ for(i = 1; i < KBDMAXKEYS; i++)
+ {
+ keytype = kbmapp->type;
+
+ if(keytype)
+ {
+ if(keytype & KBD_OVERLOAD)
+ printf("! %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);
+ else
+ printf("- %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);
+
+ switch(keytype & KBD_MASK)
+ {
+
+ case KBD_NUM:
+ case KBD_ASCII:
+ case KBD_FUNC:
+ case KBD_KP:
+ case KBD_CURSOR:
+ case KBD_RETURN: /* ??? */
+
+ if(kbmapp->subu == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->unshift));
+ else
+ printf("Function() ");
+
+ if(kbmapp->subs == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->shift));
+ else
+ printf("Function() ");
+
+ if(kbmapp->subc == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->ctrl));
+ else
+ printf("Function() ");
+
+ if(altgr_defined)
+ {
+ if(kbmapp->suba == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->altgr));
+ else
+ printf("Function() ");
+ }
+ break;
+ }
+ putchar('\n');
+ }
+ kbmapp++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * show delay and rate values for keyboard
+ *---------------------------------------------------------------------------*/
+showtypeamatic(kbfd)
+int kbfd;
+{
+ static char *delaytab[] = {
+ "250",
+ "500",
+ "750",
+ "1000"
+ };
+
+ static char *ratetab[] = {
+ "30.0",
+ "26.7",
+ "24.0",
+ "21.8",
+ "20.0",
+ "18.5",
+ "17.1",
+ "16.0",
+ "15.0",
+ "13.3",
+ "12.0",
+ "10.9",
+ "10.0",
+ "9.2",
+ "8.6",
+ "8.0",
+ "7.5",
+ "6.7",
+ "6.0",
+ "5.5",
+ "5.0",
+ "4.6",
+ "4.3",
+ "4.0",
+ "3.7",
+ "3.3",
+ "3.0",
+ "2.7",
+ "2.5",
+ "2.3",
+ "2.1",
+ "2.0"
+ };
+
+ int cur_typemat_val;
+ int delay, rate;
+
+ if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDGTPMAT failed");
+ exit(1);
+ }
+
+ delay = ((cur_typemat_val & 0x60) >> 5);
+ rate = cur_typemat_val & 0x1f;
+
+ printf("\nDisplaying the current keyboard typematic values:\n\n");
+ printf("The delay-until-repeat time is [ %s ] milliseconds\n",delaytab[delay]);
+ printf("The repeat-rate is [ %s ] characters per second\n\n",ratetab[rate]);
+}
+
+/*---------------------------------------------------------------------------*
+ * set repeat feature on/off
+ *---------------------------------------------------------------------------*/
+setrepeat(kbfd, tf)
+int kbfd;
+int tf;
+{
+ int srepsw_val;
+
+ if(tf == 1)
+ srepsw_val = KBD_REPEATON;
+ else
+ srepsw_val = KBD_REPEATOFF;
+
+ if(ioctl(kbfd, KBDSREPSW, &srepsw_val) < 0)
+ {
+ perror("kcon: ioctl KBDREPSW failed");
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set delay and rate values for keyboard
+ *---------------------------------------------------------------------------*/
+settypeam(kbfd, delay, rate)
+int kbfd;
+int delay;
+int rate;
+{
+ int cur_typemat_val;
+ int new_typemat_val;
+
+ if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDGTPMAT failed");
+ exit(1);
+ }
+
+ if(delay == -1)
+ delay = (cur_typemat_val & 0x60);
+ else
+ delay = ((delay << 5) & 0x60);
+
+ if(rate == -1)
+ rate = (cur_typemat_val & 0x1f);
+ else
+ rate &= 0x1f;
+
+ new_typemat_val = delay | rate;
+
+ if((ioctl(kbfd, KBDSTPMAT, &new_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDSTPMAT failed");
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * remap keyboard from keycap entry
+ *---------------------------------------------------------------------------*/
+remapkeys(kbfd, map)
+int kbfd;
+char *map;
+{
+ char cap_entry[1024];
+ int ret;
+ char keyflag[128];
+ int i;
+
+ /* try to find the entry */
+
+ ret = kgetent(cap_entry, map);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "kcon: keycap database not found or not accessible!\n");
+ exit(1);
+ }
+ else if(ret == 0)
+ {
+ fprintf(stderr, "kcon: keycap entry [%s] not found in database!\n", map);
+ exit(1);
+ }
+
+ /* set default mapping */
+
+ if((ioctl(kbfd, KBDDEFAULT)) < 0)
+ {
+ perror("kcon: ioctl KBDDEFAULT failed");
+ exit(1);
+ }
+
+ /* DE flag present? */
+
+ if(kgetflag("de"))
+ return;
+
+ for(i = 0; i < KBDMAXKEYS; i++)
+ keyflag[i] = 0;
+
+ set_lock(keyflag, kbfd);
+
+ set_shift(keyflag, kbfd);
+
+ set_char(keyflag, kbfd);
+}
+
+/*---------------------------------------------------------------------------*
+ * care for lock keys
+ *---------------------------------------------------------------------------*/
+set_lock(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ struct kbd_ovlkey entry;
+
+ struct {
+ char *ch;
+ u_short typ;
+ } lock[] =
+ {
+ "ca", KBD_CAPS,
+ "sh", KBD_SHFTLOCK,
+ "nl", KBD_NUMLOCK,
+ "sc", KBD_SCROLL
+ };
+
+
+ for(i = 0; i < 4; i++)
+ {
+ int n;
+
+ sprintf(cap, "%s", lock[i].ch);
+
+ n = kgetnum(cap);
+
+ if(n > 0)
+ {
+ if (keyflag[n])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
+ exit(1);
+ }
+ keyflag[n] = 1;
+
+ entry.keynum = n;
+ entry.type = lock[i].typ;
+
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * care for shifting keys
+ *---------------------------------------------------------------------------*/
+set_shift(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ struct kbd_ovlkey entry;
+
+ struct {
+ char ch;
+ u_short typ;
+ } shift[] =
+ {
+ 'm', KBD_META,
+ 'l', KBD_ALTGR,
+ 'h', KBD_SHIFT,
+ 't', KBD_CTL
+ };
+
+ for(i = 0; i < 4; i++)
+ {
+ for(j = 1; j < 10; j++)
+ {
+ int n;
+
+ sprintf(cap, "%c%d", shift[i].ch,j);
+
+ n = kgetnum(cap);
+
+ if (n >= 0)
+ {
+ if (keyflag[n])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
+ exit(1);
+ }
+ keyflag[n] = 1;
+
+ entry.keynum = n;
+ entry.type = shift[i].typ;
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * care for normal keys
+ *---------------------------------------------------------------------------*/
+set_char(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ int setflag;
+ char *addr_str;
+ char *new_str;
+ struct kbd_ovlkey entry;
+
+ struct {
+ char *addr;
+ char ch;
+ } standard[] = {
+ 0, 'D',
+ &entry.unshift[0], 'K',
+ &entry.shift[0], 'S',
+ &entry.ctrl[0], 'C',
+ &entry.altgr[0], 'A'
+ };
+
+ for(i = 1; i < KBDMAXKEYS; i++)
+ {
+ setflag = 0;
+
+ entry.keynum = i;
+
+ if((ioctl(kbfd, KBDGOKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDGOKEY failed");
+ exit(1);
+ }
+
+ entry.type = KBD_ASCII;
+
+ for(j = 0; j < 5; j++)
+ {
+ sprintf(cap, "%c%d", standard[j].ch,i);
+
+ if((j == 0) && (kgetflag(cap)))
+ {
+ /* delete a key */
+
+ entry.type = KBD_NONE;
+ setflag = 1;
+ goto setit;
+
+ }
+ else
+ {
+ addr_str = standard[j].addr;
+ if(new_str = kgetstr(cap, &addr_str))
+ {
+ if(strlen(new_str) > KBDMAXOVLKEYSIZE)
+ {
+ fprintf(stderr, "kcon: database entry string [%s] longer than max [%d]!\n",new_str,KBDMAXOVLKEYSIZE);
+ exit(1);
+ }
+ setflag = 1;
+ }
+ }
+ }
+
+setit: if (setflag)
+ {
+ if (keyflag[i])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",i);
+ exit(1);
+ }
+ keyflag[i] = 1;
+
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+}
+
+/*------------------- EOF ------------------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/Makefile b/usr.sbin/pcvt/keycap/Makefile
new file mode 100644
index 0000000..b4e4350
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+LIB= keycap
+MAN= keycap.3
+MLINKS+= keycap.3 kgetent.3 \
+ keycap.3 kgetnum.3 \
+ keycap.3 kgetflag.3 \
+ keycap.3 kgetstr.3
+MAN+= man5/keycap.5
+SRCS= keycap.c
+
+KEYCAPSRC= keycap.src
+CAPDIR= /usr/share/misc
+CAPPATH= $(CAPDIR)/keycap.pcvt
+
+CFLAGS+= -DKEYCAP_PATH=\"$(CAPPATH)\"
+
+#CLEANFILES+= keycap.0 man5/keycap.0
+
+beforeinstall:
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \
+ ${.CURDIR}/${KEYCAPSRC} ${DESTDIR}${CAPPATH}
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/pcvt/keycap/keycap.3 b/usr.sbin/pcvt/keycap/keycap.3
new file mode 100644
index 0000000..19348d2
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.3
@@ -0,0 +1,125 @@
+.\"
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided 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.
+.\"
+.\" @(#)keycap.3, 3.00, Last Edit-Date: [Sun Jan 2 13:46:43 1994]
+.\" $FreeBSD$
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 3
+.Os
+.Sh NAME
+.Nm kgetent ,
+.Nm kgetnum ,
+.Nm kgetflag ,
+.Nm kgetstr
+.Nd routines for accessing the keycap database
+.Sh SYNOPSIS
+.Ft int
+.Fn kgetent "char *bp" "char *name"
+.Ft int
+.Fn kgetnum "char *id"
+.Ft int
+.Fn kgetflag "char *id"
+.Ft char *
+.Fn kgetstr "char *id" "char **area"
+.Sh DESCRIPTION
+These functions extract and use capabilities from a keyboard capability data
+base, usually
+.Pa /usr/share/misc/keycap.pcvt ,
+the format of which is described in
+.Xr keycap 5 .
+.Pp
+The
+.Fn kgetent
+function
+extracts the entry for keyboard mapping
+.Fa name
+into the buffer at
+.Fa bp .
+The
+.Fa bp
+argument
+should be a character buffer of size
+1024 and must be retained through all subsequent calls to
+.Fn kgetnum ,
+.Fn kgetflag ,
+and
+.Fn kgetstr .
+The
+.Fn kgetent
+function
+returns \-1 if none of the
+.Nm keycap
+data base files could be opened,
+0 if the map name given does not have an entry,
+and 1 if all goes well.
+.Pp
+The
+.Fn kgetnum
+function
+gets the numeric value of capability
+.Fa id ,
+returning \-1 if is not given for the map.
+.Pp
+The
+.Fn kgetflag
+function
+returns 1 if the specified capability is present in
+the map's entry, 0 if it is not.
+.Pp
+The
+.Fn kgetstr
+function
+returns the string value of the capability
+.Fa id ,
+places it in the buffer at
+.Fa area ,
+and advances the
+.Fa area
+pointer.
+The
+.Fn kgetstr
+function
+returns
+.Dv NULL
+if the capability was not found.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+Keyboard capabilities database (if nothing else chosen during installation).
+.El
+.Sh SEE ALSO
+.Xr kcon 1 ,
+.Xr keycap 5
diff --git a/usr.sbin/pcvt/keycap/keycap.c b/usr.sbin/pcvt/keycap/keycap.c
new file mode 100644
index 0000000..9c276a3
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.c
@@ -0,0 +1,383 @@
+/*-
+ * Copyright (c) 1992, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 rcsid[] =
+ "$FreeBSD$";
+#endif
+
+static char *id =
+ "@(#)keycap.c, 3.20, Last Edit-Date: [Tue Dec 20 14:51:50 1994]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * keycap.c Keyboard capabilities database handling
+ * -------------------------------------------------------
+ *
+ * converted from printcap by Holger Veit (veit@du9ds3.uni-duisburg.de)
+ *
+ * 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.
+ *
+ * modified by Hellmuth Michaelis (hm@hcshh.hcs.de) to fit into the
+ * vt220 driver pcvt 2.0 distribution
+ *
+ * -hm header conversion & cosmetic changes for pcvt 2.0 distribution
+ * -hm debugging remapping
+ * -hm cleaning up from termcap ....
+ * -hm split off header file keycap.h
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "keycap.h"
+
+#define KEYCAP_BUFSIZ 1024
+
+#define MAXHOP 32 /* max number of tc= indirections */
+
+char *getenv();
+
+static FILE *pfp = NULL; /* keycap data base file pointer */
+static char *tbuf;
+static int hopcount; /* detect infinite loops in keycap, init 0 */
+
+static int knchktc();
+static int knamatch();
+static char *kdecode();
+
+/*---------------------------------------------------------------------------*
+ * match a name
+ *---------------------------------------------------------------------------*/
+static char *nmatch(id,cstr)
+char *id,*cstr;
+{
+ register n = strlen(id);
+ register char *c = cstr+n;
+
+ if (strncmp(id,cstr,n)==0 &&
+ (*c==':' || *c=='|' || *c=='=' || *c=='#') || *c=='@')
+ return c;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * Get an entry for keyboard name in buffer bp from the keycap file.
+ * Parse is very rudimentary, we just notice escaped newlines.
+ *---------------------------------------------------------------------------*/
+kgetent(bp, name)
+char *bp, *name;
+{
+ register char *cp;
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[KEYCAP_BUFSIZ];
+ char *cp2;
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+
+ tf = open(KEYCAP_PATH, 0);
+
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, KEYCAP_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+KEYCAP_BUFSIZ) {
+ write(STDERR_FILENO, "Keycap entry too long\n", 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (knamatch(name)) {
+ close(tf);
+ return(knchktc());
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * knchktc: 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. Note that this works because of the left to right scan.
+ *---------------------------------------------------------------------------*/
+static int knchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar keyboard */
+ char tcbuf[KEYCAP_BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(STDERR_FILENO, "Bad keycap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return(1);
+ strcpy(tcname,p+3);
+ q = tcname;
+ while (q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(STDERR_FILENO, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (kgetent(tcbuf, tcname) != 1)
+ return(0);
+ for (q=tcbuf; *q != ':'; q++)
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > KEYCAP_BUFSIZ) {
+ write(STDERR_FILENO, "Keycap entry too long\n", 23);
+ q[KEYCAP_BUFSIZ - (p-tbuf)] = 0;
+ }
+ strcpy(p, q+1);
+ tbuf = holdtbuf;
+ return(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * knamatch deals with name matching. The first field of the keycap 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.
+ *---------------------------------------------------------------------------*/
+static int knamatch(np)
+char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#' || *Bp == 0)
+ 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 keycap file
+ * in octal.
+ *---------------------------------------------------------------------------*/
+static char *kskip(bp)
+char *bp;
+{
+ while (*bp && *bp != ':')
+ bp++;
+ 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.
+ *---------------------------------------------------------------------------*/
+int kgetnum(id)
+char *id;
+{
+ register int i, base;
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if ((xp=nmatch(id,bp)) == 0)
+ continue;
+ bp = xp; /* we have an entry */
+ 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 kgetflag(id)
+char *id;
+{
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (!*bp)
+ return (0);
+ if ((xp=nmatch(id,bp)) != 0) {
+ bp = xp;
+ 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 *kgetstr(id, area)
+char *id;
+char **area;
+{
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (!*bp)
+ return (0);
+ if ((xp = nmatch(id,bp)) == 0)
+ continue;
+ bp = xp;
+ if (*bp == '@')
+ return(0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (kdecode(bp, area));
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * kdecode does the grung work to decode the string capability escapes.
+ *---------------------------------------------------------------------------*/
+static char *kdecode(str, area)
+char *str;
+char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':') {
+ 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;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
+
+/*-------------------------------- EOF --------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/keycap.h b/usr.sbin/pcvt/keycap/keycap.h
new file mode 100644
index 0000000..1dc4c3e
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1992, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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.
+ *
+ * @(#)keycap.h, 3.20, Last Edit-Date: [Tue Dec 20 14:52:11 1994]
+ */
+
+#ifndef _KEYCAP_H_
+#define _KEYCAP_H_
+
+int kgetent( char*, char* );
+int kgetnum( char* );
+int kgetflag( char* );
+char *kgetstr( char*, char** );
+
+#endif /* _KEYCAP_H_ */
+
+/*-------------------------------- EOF -------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/keycap.src b/usr.sbin/pcvt/keycap/keycap.src
new file mode 100644
index 0000000..a991264e
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.src
@@ -0,0 +1,627 @@
+#
+# Copyright (c) 1992, 2002 Hellmuth Michaelis
+#
+# Copyright (c) 1992, 1994 Joerg Wunsch and Holger Veit
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided 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 Hellmuth Michaelis,
+# Joerg Wunsch and Holger Veit.
+# 4. 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 REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+#---------------------------------------------------------------------------
+#
+# keyboard mappings for vt220 emulator pcvt 3.00
+# ----------------------------------------------
+#
+# Last Edit-Date: [Wed Apr 24 16:27:35 2002]
+#
+# $FreeBSD$
+#
+#---------------------------------------------------------------------------
+
+df|default|default entry:\
+ :de:
+
+tt|test|Test entry which swaps y and z:\
+ :K22=z:S22=Z:C22=\032:\
+ :K46=y:S46=Y:C46=\031:
+
+# from codrv, untested
+be|belgium|Belgian mapping:\
+ :K1=\262:S1=\263:\
+ :K2=&:S2=1:A2=|:\
+ :K3=\351:S3=2:A3=@:\
+ :K4=":S4=3:A4=#:\
+ :K5=':S5=4:\
+ :K6=(:S6=5:\
+ :K7=\247:S7=6:A7=\136:\
+ :K8=\350:S8=7:\
+ :K9=!:S9=8:\
+ :K10=\347:S10=9:A10={:\
+ :K11=\340:S11=0:A11=}:\
+ :K12=):S12=\260:\
+ :K13=-:S13=_:\
+ :K17=a:S17=A:C17=^A:\
+ :K18=z:S18=Z:C18=^z:\
+ :K27=\136:S27=\250:A27=[:p1#27:\
+ :K28=$:S28=*:A28=]:\
+ :K31=q:S31=Q:C31=^q:\
+ :K40=m:S40=M:C40=^m:\
+ :K41=\371:S41=%:A41=':p2#41:\
+ :K42=\265:S42=\243:A42=\264:p3#42:\
+ :K45=<:S45=>:A45=\\:\
+ :K46=w:S46=W:C46=^w:\
+ :K52=,:S52=\077:\
+ :K53=;:S53=.:\
+ :K54=\072:S54=/:\
+ :K55==:S55=+:A55=~:\
+ :l1#62:a0:
+
+# from codrv, untested
+ca|canadafr|Canadian French mapping:\
+ :K1=#:S1=|:A1=\\:\
+ :A2=\261:S3=":A3=@:S4=/:A4=\243:A5=\242:\
+ :A6=\244:S7=\077:A7=\254:S8=&:A8=\246:S9=*:A9=\262:\
+ :S10=(:A10=\263:S11=):A11=\274:\
+ :K12=-:S12=_:A12=\275:\
+ :K13==:S13=+:A13=\276:\
+ :A25=\247:A26=\266:\
+ :K27=\136:S27=\136:A27=[:\
+ :K28=\270:S28=\250:A28=]:p1#28:\
+ :A40=~:K41=`:S41=`:A41={:\
+ :K42=<:S42=>:A42=}:\
+ :K45=\253:S45=\273:A45=\260:\
+ :A49=\253:A50=\273:A51=\260:\
+ :A52=\265:K53=,:S53=,:A53=-:\
+ :K54=\351:S54=\311:A54=':\
+ :l1#62:a0:
+
+# from codrv, untested
+c1|swissde|Swiss German mapping:\
+ :K1=\247:S1=\260:p1#1:\
+ :S2=+:A2=|:S3=":A3=@:S4=*:A4=#:S5=\347:S7=&:A7=\254:\
+ :S8=/:A8=\246:S9=(:A9=\242:S10=):S11==:\
+ :K12=`:S12=\077:A12=':p2#12:\
+ :K13=^:S13=\\:A13=~:p3#13:\
+ :K22=z:S22=Z:C22=\032:\
+ :S27=\350:K27=\374:A27=[:\
+ :K28=\250:S28=!:A28=]:\
+ :S40=\351:K40=\366:\
+ :S41=\340:K41=\344:A41={:\
+ :K42=$:S42=\243:A42=}:\
+ :K45=<:S45=>:A45=\\:\
+ :K46=y:S46=Y:C46=\031:
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+c2|swissfr|Swiss French mapping:\
+ :K27=\350:S27=\374:A27=[:\
+ :K40=\351:S40=\366:\
+ :K41=\340:S41=\344:A41={:\
+ :tc=swissde:
+
+# more programmer-like than an original German kbd, you needn't
+# have gum-fingers to get `{}' and the like:-)
+# maps: ae -> [, oe -> \, ue -> ], Ae -> {, Oe -> |, Ue -> }
+# umlaute are available as AltGr- and Control-Mappings
+# also maps Pause -> ^Z
+#
+# (from Joerg Wunsch)
+#
+# l1/m1 bindings: left Alt is AltGr
+# Emacs functions:
+# C79/C89: ctrl-{leftarrow,rightarrow} {backward,forward} word
+# A79/A89: {backward,forward} sexp
+# C83/C84: ctrl-{uparrow,downarrow} {backward,forward} window
+#
+de-prog|germany-prog|programmer's mapping for german keyboard:\
+ :K27=]:S27=}:A27=\374:C27=\334:\
+ :K40=\\:S40=|:A40=\366:C40=\326:\
+ :K41=[:S41={:A41=\344:C41=\304:\
+ :K126=\032:C126=\003:\
+ :tc=de:
+# :l1#60:l2#62:\
+# :C79=^[B:K79=^[[D:S79=^[OD:A79=^[^B:\
+# :C89=^[F:K89=^[[C:S89=^[OC:A89=^[^F:\
+# :C83=^U-1^XO:K83=^[[A:S84=^[OA:\
+# :C84=^XO:K84=^[[B:S84=^[OB:\
+
+de|germany|German mapping for MF II-Keyboard:\
+ :K1=\136:S1=\260:\
+ :S3=\042:S4=\247:S7=&:S8=/:S9=(:S10=):S11=\075:\
+ :A3=\262:A4=\263:A8={:A9=[:A10=]:A11=}:A12=\134:\
+ :K12=\337:S12=?:C12=\034:\
+ :K13=':S13=`:\
+ :A17=\100:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:\
+ :K28=+:S28=*:A28=\176:\
+ :K29=\043:S29=':A29=\174:\
+ :K40=\366:S40=\326:\
+ :K41=\344:S41=\304:\
+ :A45=\174:\
+ :K46=y:S46=Y:C46=\031:\
+ :A52=\265:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:
+
+de-at|germany-at|German mapping for AT-Keyboard:\
+ :K1=<:S1=>:\
+ :S3=\042:\
+ :S4=\247:\
+ :S7=&:\
+ :S8=/:\
+ :S9=(:\
+ :S10=):\
+ :S11=\075:\
+ :K12=\337:S12=?:C12=\034:\
+ :K13=':S13=`:\
+ :K14=#:S14=\136:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:\
+ :K28=+:S28=*:\
+ :K29=\043:S29=':\
+ :K40=\366:S40=\326:\
+ :K41=\344:S41=\304:\
+ :K46=y:S46=Y:C46=\031:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+
+# from codrv, untested
+# Includes improvements by Thomas Hiller (hiller@fzi.de)
+# and Andreas Israel (ai@hrz.tu-chemnitz.de)
+de-hi|germany-hiller|yet another German mapping:\
+ :K1=\136:S1=\260:C1=|:\
+ :S3=\042:S4=#:S7=&:S8=/:S9=(:S10=):S11=\075:\
+ :A8={:A9=[:A10=]:A11=}:A17=@:A28=~:\
+ :K12=\337:S12=\077:C12=\036:A12=\\:\
+ :K13=':S13=`:C13=\134:p1#13:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:C27=\035:\
+ :K28=+:S28=*:C28=\000:\
+ :K29=<:S29=>:C29=\134:\
+ :K40=\366:S40=\326:C40=\034:\
+ :K41=\344:S41=\304:C41=\033:\
+ :K46=y:S46=Y:C46=\031:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Contribution by Thomas Hiller (hiller@fzi.de)
+# "K42 may not work on 102 keys kbds, K29 seems to work"
+d1|de-102|german with mf2:\
+ :K29=#:\
+ :K42=#:\
+ :K45=<:S45=>:A45=|:\
+ :tc=germany:
+
+# from codrv, untested
+hv|holgi|Holgi's special MF1 keyboard mapping:\
+ :K1=<:S1=>:C1=|:\
+ :K29=#:S29=\136:A29=\\:C29=~:\
+ :tc=germany:
+
+# from codrv, untested
+# Contributed by Andreas Israel (ai@hrz.tu-chemnitz.de)
+de-ai|nixmf2|ct22|nix|nix7|German Nixdorf MF2:\
+ :A28=~:\
+ :K29=#:S29=':\
+ :K45=<:S45=>:A45=|:\
+ :tc=germany:
+
+# from codrv, untested
+dk|denmark|Danish mapping:\
+ :K1=\275:S1=\247:\
+ :S3=":A3=@:\
+ :A4=\243:\
+ :S5=\244:A5=$:\
+ :S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=+:S12=\077:\
+ :K13=':S13=`:A13=|:p1#13:\
+ :K27=\345:S27=\305:\
+ :K28=\250:S28=\136:A28=~:p2#28:\
+ :K40=\346:S40=\306:\
+ :K41=\370:S41=\330:\
+ :K42=:S42=*:\
+ :K45=<:S45=>:A45=\\:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Finnish keyboard map with 7-bit versions of the national
+# chars. The Latin1 chars are available with Alt-7, Alt-8, etc
+# (where normally you would have the 7-bit ones).
+# Makes C/C++ programming more comfortable, since the 7-bit chars
+# (|\{[}]) are needed much more often than the Latin1 chars.
+# -- Petri.Wessman@hut.fi
+fi|finland|finland7b|finland-ascii|Finnish ASCII mapping:\
+ :l1#60:l2#62:\
+ :A8=\344:A9=\304:A10=\305:A11=\345:A12=\326:A13=\366:\
+ :K40=|:S40=\\:K41={:S41=[:K27=}:S27=]:\
+ :S1=\275:K1=\247:\
+ :S3=":A3=@:\
+ :A4=\243:\
+ :S5=$:A5=$:\
+ :S7=&:S8=/:S9=(:S10=):S11==:\
+ :K12=+:S12=\077:\
+ :K13=':S13=`:\
+ :K28=~:S28=\136:A28=~:\
+ :K29=':S29=*:\
+ :K45=<:S45=>:A45=|:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :a0:
+
+# from codrv, untested
+# Finnish keyboard map with the Latin1 national chars in
+# their "right" place. --Petri.Wessman@hut.fi
+f1|finland8b|finland-latin1|Finnish Latin1 mapping:\
+ :A8={:A9=[:A10=]:A11=}:A12=\\:\
+ :K40=\366:S40=\326:K41=\344:S41=\304:K27=\345:S27=\305:\
+ :tc=finland:
+
+
+# French keyboard mapping
+# From Matthieu Herrb <matthieu@laas.fr>
+# For 102 keys keyboards, produces 8 bits characters
+# with ISO Latin-1 encoding
+f8|france-iso-8859-1|French ISO 8859-1 102 keys keyboard:\
+ :l1#62:\
+ :K1=\262:S1=:\
+ :K2=&:S2=1:\
+ :K3=\351:S3=2:C3=\211:A3=~:\
+ :K4=":S4=3:A4=#:\
+ :K5=':S5=4:A5={:\
+ :K6=(:S6=5:A6=[:\
+ :K7=-:S7=6:C7=\036:A7=|:\
+ :K8=\350:S8=7:C8=\210:A8=`:\
+ :K9=_:S9=8:C9=\037:A9=\\:\
+ :K10=\347:S10=9:C10=\207:A10=\136:\
+ :K11=\340:S11=0:C11=\340:A11=@:\
+ :K12=):S12=\260:A12=]:\
+ :A13=}:\
+ :K17=a:S17=A:C17=\001:\
+ :K18=z:S18=Z:C18=\032:\
+ :D27:\
+ :K28=$:S28=\243:\
+ :K29=*:S29=\265:\
+ :K31=q:S31=Q:C31=\021:\
+ :K40=m:S40=M:C40=\015;\
+ :K41=\371:C41=\231:S41=%:\
+ :K42=*:S42=\265:\
+ :K46=w:S46=W:C46=\027:\
+ :K52=,:S52=?:\
+ :K53=;:S53=.:\
+ :K54=\072:S54=/:C54=\037\
+ :K55=!:S55=\266:
+
+# fr|france|French mapping:\
+# :de:
+#
+# from codrv, untested
+# f1|france120|French 120 mapping:\
+# :tc=belgium:
+#
+#
+# from codrv, untested
+#f2|france189|French 189 mapping:\
+# :S1=:A1=':p3#1:\
+# :A2=:A3=~:A5={:A6=[:A7=|:A8=`:\
+# :S9=_:A9=\\:A10=\136:A11=@:A12=]:A13=}:\
+# :A27=:A28=\244:A41=:A42=:\
+# :A45=:!:S45=\247:\
+# :tc=belgium:
+
+# From: Andy Duplain, duplain@rtf.bt.co.uk
+gb|greatbritain|British mapping for MF-2 keyboard:\
+ :S1=|:S3=":C3=2:C12=-:S41=@:K42=#:S42=~:C42=#:K45=\\:S45=|:C45=\\:
+
+# from codrv, untested
+# This entry has been corrected by Mike Trim (mtrim@crucible.demon.co.uk)
+# (hv's comment: For the keys # and ~ you might also check the following
+# line
+# :K42=#:S42=~:\
+# Also I think I was wrong with the ALTGR key. If you need one, add this:
+# :l1#62:
+g1|greatbritain166|British 166 mapping:\
+ :K1=`:S1=\254:A1=|:\
+ :S3=":S4=\243:\
+ :K41=':S41=@:\
+ :K29=#:S29=~:\
+ :K45=\\:S45=|:
+
+# from codrv, untested
+g2|greatbritain168|British 168 mapping:\
+ :K1=\\:S1=|:\
+ :S3=":S4=\243:S7=&:S8=:S9=(:S10=):A10=\261:\
+ :S11=#:A11=\260:S12=:K13=\254:S13=-:\
+ :K27=@:S27=`:\
+ :K28=[:S28={:A28=~:\
+ :K40=;:S40=+:\
+ :K41=\072:S41=*:A41=\136:\
+ :K42=]:S42=}:\
+ :K45=|:S45=_:\
+ :A52=\265:\
+ :l1#62:a0:
+
+# from codrv, untested
+is:iceland:Island mapping:\
+ :K1=\260:S1=\250:p1#1:\
+ :S2=":S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=\\:S12=\326:\
+ :K13=':S13=`:A13=|:\
+ :A17=@:K27=\360:S27=\320:\
+ :K28=':S28=\077:A28=~:\
+ :K40=\346:S40=\306:\
+ :K41=':S41=':A41=\136:p2#41:\
+ :K42=+:S42=*:A42=`:p3#42:\
+ :K45=<:S45=>:A45=|:\
+ :S53=;:S54=\072:\
+ :K54=\376:S54=\336:\
+ :K104=,:104=,:\
+ :l1#62:a0:
+
+# from codrv, untested
+i1|italy141|Italian 141 mapping:\
+ :K1=\\:S1=|:\
+ :S3=":S4=\243:S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=':A12=`:S12=\077:\
+ :K13=\354:S13=\136:A13=~:\
+ :K27=\350:S27=\351:\A27=[:\
+ :K28=+:S28=*:A28=]:\
+ :K40=\362:S40=\347:A40=@:\
+ :K41=\340:S41=\260:A41=#:\
+ :K29=\371:S29=\247:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+i2|italy142|Italian 142 mapping:\
+ :A8={:A9=[:A10=]:A11=}:\
+ :A17=@:A27=:A28=~:A40=:A41=:\
+ :tc=italy141:a0:
+
+# from codrv, untested
+nl|netherlands|Dutch mapping:\
+ :K1=@:S1=\247:A1=\254:\
+ :A2=\271:S3=":A3=\262:S4=#:A4=\263:A5=\274:A6=\275:S7=&:\
+ :A7=\276:S8=_:A8=\243:S9=(:A9={:S10=):A10=}:S11=':\
+ :K12=/:S12=\077:A12=\\:\
+ :K13=\260:S13=~:A13=\270:p1#13:\
+ :K20=\266:K27=\250:S27=^:p2#27:\
+ :K28=*:S28=|:\
+ :K40=+:S40=\261:\
+ :K41=':S41=`:\
+ :K42=<:S42=>:\
+ :K45=[:S45=]:A45=|:\
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :K104=,:S104=,:\
+ :l1#62:a0:
+
+# Norwegian keyboard map with national characters.
+# Anders Nordby, anders@fix.no
+no|norway|Norwegian 8bit mapping ISO 8859-1:\
+ :K1=\174:S1=\247:\
+ :K2=\61:S2=\41:\
+ :K3=\62:S3=\42:A3=\100:\
+ :K4=\63:S4=\43:A4=\243:\
+ :K5=\64:S5=\244:A5=\44:\
+ :K6=\65:S6=\45:\
+ :K7=\66:S7=\46:\
+ :K8=\67:S8=\57:A8=\173:\
+ :K9=\70:S9=\50:A9=\133:\
+ :K10=\71:S10=\51:A10=\135:\
+ :K11=\60:S11=\75:A11=\175:\
+ :K12=\53:S12=\77:\
+ :K13=\134:S13=\140:A13=\264:\
+ :K28=\250:S28=\136:A28=\176:\
+ :K29=\47:S29=\52:\
+ :K54=\56:S54=\72:\
+ :K53=\54:S53=\73:\
+ :K55=\55:S55=\137:\
+ :K41=\346:S41=\306:\
+ :K40=\370:S40=\330:\
+ :K27=\345:S27=\305:\
+ :l1#62:a0:
+
+# from codrv, untested
+pt|portugal|Portugesian mapping:\
+ :K1=\\:S1=|:\
+ :S3=":A3=@:A4=\243:A5=\247:S7=&:S8=/:A8={:S9=(:A9=[:\
+ :S10=):A10=]:S11=}:A11==:\
+ :K12=':S12=\077:\
+ :K13=\253:S13=\273:\
+ :K40=\347:S40=\307:\
+ :K41=\272:S41=\252:\
+ :K42=~:S42=^:p1#42:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+es|spain|Spainish mapping:\
+ :K1=\272:S1=\252:A1=\\:\
+ :A2=|:S3=":A3=@:S4=:A4=#:S7=&:A7=\254:S8=/:S9=(:S10=):S11==:\
+ :K12=':S12=\077:p1#12:\
+ :K13=\277:S13=\241:\
+ :K27=`:S27=^:A27=[:p2#27:\
+ :K28=+:S28=*:A28=]:\
+ :K40=\361:S41=\321:\
+ :K41=/:S41=\250:A41={:p3#41:\
+ :K42=\347:S42=\307:A42=}:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :K104=,:S104=,:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Contributed by Mats O Jansson, moj@stacken.kth.se
+# "Here is my map, rather large, but i want ALTGR normaly to be dead.
+# Only seven bit national characters have been placed where national characters
+# is on the keyboard."
+# We can help this man, just use the 'a0' capability -hv-
+s1|sweden1|Swedish mapping:\
+ :l1#62:a0:\
+ :D1:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=]:K28=:\
+ :S27=}:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :K40=\134:K41=[:\
+ :S40=|:S41={:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# from codrv, untested
+# Contributed by Mats O Jansson, moj@stacken.kth.se
+# "Here is my map, rather large, but i want ALTGR normaly to be dead.
+# Only seven bit national characters have been placed where national
+# characters is on the keyboard."
+# We can help this man, just use the 'a0' capability -hv-
+# Corrected by Paul Pries, 5322@msg.abc.se (Some national shifts were
+# wrong).
+sa|sweden1a|Swedish 7bit mapping ISO 646:\
+ :l1#62:a0:\
+ :D1:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=}:K28=:\
+ :S27=]:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :S40=\134:S41=[:\
+ :K40=|:K41={:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# from codrv, untested
+# Swedish keyboard map with national characters.
+# Paul Pries, 5322@msg.abc.se
+s2|sweden2|Swedish 8bit mapping ISO 8859-1:\
+ :l1#62:\
+ :K1=\247:S1=:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A4=\234:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=\206:K28=:\
+ :S27=\217:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :S40=\231:S41=\216:\
+ :K40=\224:K41=\204:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# Swedish keyboard map with national characters.
+# Paul Pries, 5322@msg.abc.se
+# Corrected by Mats O Jansson, moj@stacken.kth.se (Some national was CP850
+# and not ISO 8859-1 as said).
+s8|sweden8|Swedish 8bit mapping ISO 8859-1 and not CP850 national characters:\
+ :A8={:A9=[:A10=]:A11=}:A12=\\:\
+ :K40=\366:S40=\326:K41=\344:S41=\304:K27=\345:S27=\305:\
+ :tc=sweden2:
+
+#
+# tg: my idiosyncratic mappings (thomas@ghpc8.ihf.rwth-aachen.de)
+#
+# the six function keys above the cursor keys are arranged
+# identical to a real VT220:
+#
+# find insert remove
+# select up down
+#
+# since i don't have a use for the numbers on the keypad,
+# i map NumLock, /, *, - to PF1-PF4;
+# + is mapped to SS3 l, shifted + is mapped to SS3 m
+#
+# they convinced me finally to add some support for german umlauts.
+# so, i stole the mapping from jörg wunsch's de-prog entry.
+#
+# tg
+#
+
+tg:\
+ :l1#62:\
+ :A12=\337:\
+ :A27=\374:C27=\334:\
+ :A40=\366:C40=\326:\
+ :A41=\344:C41=\304:\
+ :K126=\032:C126=\003:\
+ :K75=[1~:S75=[1~:C75=[1~:\
+ :K76=[4~:S76=[4~:C76=[4~:\
+ :K80=[2~:S80=[2~:C80=[2~:\
+ :K81=[5~:S81=[5~:C81=[5~:\
+ :K85=[3~:S85=[3~:C85=[3~:\
+ :K86=[6~:S86=[6~:C86=[6~:\
+ :K90=OP:S90=OP:C90=OP:\
+ :K95=OQ:S95=OQ:C95=OQ:\
+ :K100=OR:S100=OR:C100=OR:\
+ :K104=On:S104=On:C104=On:\
+ :K105=OS:S105=OS:C105=OS:\
+ :K106=Ol:S106=Om:\
+ :K108=OM:S108=OM:C108=OM:
+
+us|usa|United States mapping:\
+ :de:
+
+# EOF
diff --git a/usr.sbin/pcvt/keycap/man5/keycap.5 b/usr.sbin/pcvt/keycap/man5/keycap.5
new file mode 100644
index 0000000..7e40183
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/man5/keycap.5
@@ -0,0 +1,129 @@
+.\"
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided 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.
+.\"
+.\" @(#)keycap.5, 3.00, Last Edit-Date: [Sun Jan 2 13:45:59 1994]
+.\" $FreeBSD$
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 5
+.Os
+.Sh NAME
+.Nm keycap
+.Nd keyboard mapping data base
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file
+is a data base describing keyboard mappings, used by
+.Xr kcon 1 .
+.Pp
+Entries in
+.Nm
+consist of a number of `:'-separated fields.
+The first entry for each mapping gives the names that are known for the
+mapping, separated by `|' characters.
+All names but the first and last
+should be in lower case and contain no blanks;
+the last name may well contain
+upper case and blanks for readability.
+.Sh CAPABILITIES
+.Bl -column indent indent
+.Sy "Name Type Description
+.It "de bool Resets Keyboard mapping to compiled-in default"
+.It "D<n> bool Disables key <n> completely"
+.It "
+.It "m<n> num specify key numbers for ALT keys
+.It "l<n> num specify key numbers for ALTGR keys
+.It "h<n> num specify key numbers for SHIFT keys
+.It "t<n> num specify key numbers for CONTROL keys
+.It "ca<n> num specify key number for the CAPS LOCK key
+.It "sh<n> num specify key number for the SHIFT LOCK key
+.It "nl<n> num specify key number for the NUM LOCK key
+.It "sc<n> num specify key number for the SCROLL LOCK key
+.It "
+.It "K<n> str bind a string to an unshifted (normal) key
+.It "S<n> str bind a string to a shifted key
+.It "C<n> str bind a string to a control key
+.It "A<n> str bind a string to an altgr key
+.It "
+.It "tc str Entry of similar map \- must be last."
+.El
+.Pp
+Parameter <n> describing the key number can have values from 1 to 128.
+.Pp
+A string parameter may have up to 15 characters.
+.Pp
+.Ss A Sample Entry
+The following entry, which describes a test entry, is among the very
+easy entries in the
+.Nm
+file as of this writing.
+.Pp
+.Bd -literal
+tt\||test\||Test entry which swaps y and z:\e
+ :K22=z:S22=Z:C22=\e032:\e
+ :K46=y:S46=Y:C46=\e031:
+.Ed
+.Pp
+Entries may continue onto multiple lines by giving a \e as the last
+character of a line.
+Comments may be included on lines beginning with
+.Dq # .
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+File containing keyboard mapping descriptions.
+.El
+.Sh SEE ALSO
+.Xr kcon 1 ,
+.Xr keycap 3
+.Sh EXAMPLES
+The entry
+.Dq Li l1#60
+sets the keynumber for the ALTGR key to 60.
+.Pp
+The entry
+.Dq Li K100=hugo
+binds the string 'hugo' to the key number 100.
+.Pp
+The entry
+.Dq Li K100=^D
+binds the control character EOT (0x04) to the key number 100.
+.Pp
+The entry
+.Dq Li K100=\e000
+binds the control character NUL (0x00) to the key number 100.
diff --git a/usr.sbin/pcvt/loadfont/Makefile b/usr.sbin/pcvt/loadfont/Makefile
new file mode 100644
index 0000000..50646a7
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= loadfont
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/loadfont/loadfont.1 b/usr.sbin/pcvt/loadfont/loadfont.1
new file mode 100644
index 0000000..c579bb8
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.1
@@ -0,0 +1,87 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this 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.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 17:07:57 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt LOADFONT 1
+.Os
+.Sh NAME
+.Nm loadfont
+.Nd pcvt utility for loading fonts into VGA/EGA boards
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar charsetno
+.Op Fl d Ar devicefile
+.Op Fl f Ar fontfilename
+.Op Fl i
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to load fonts needed for proper operation of the pcvt
+VT220 driver on EGA and VGA boards into the font ram of this boards.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Specifies the slot, the font is to load into.
+EGA boards have four
+slots and VGA boards have eight slots available for downloading fonts.
+.It Fl d
+Specifies the devicefile to use.
+.It Fl f
+Specifies the file which contains the font to be downloaded.
+.It Fl i
+Gives information what type(s) of font do currently reside in which slot.
+This is also the default behaviour if no options are specified on the commandline.
+.El
+.Pp
+This utility is used only on EGA and VGA boards, as MDA, HCG and CGA boards
+do not have downloadable character sets available.
+.Sh FILES
+The following fontfiles are available in the pcvt distribution:
+.Bd -literal
+/usr/share/misc/pcvtfonts/vt220l.808: 8x8 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.808: 8x8 Extension font
+/usr/share/misc/pcvtfonts/vt220l.810: 8x10 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.810: 8x10 Extension font
+/usr/share/misc/pcvtfonts/vt220l.814: 8x14 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.814: 8x14 Extension font
+/usr/share/misc/pcvtfonts/vt220l.816: 8x16 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.816: 8x16 Extension font
+.Ed
+.Sh EXAMPLES
+The command
+.Dq Li loadfont -c0 -f/usr/share/misc/pcvtfonts/vt220l.816
+loads a 8x16 font containing the standard IBM characterset II into font slot
+0 on a VGA or EGA board.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr cursor 1 ,
+.Xr scon 1 ,
+.Xr pcvt 4 ,
+.Xr ispcvt 8
diff --git a/usr.sbin/pcvt/loadfont/loadfont.c b/usr.sbin/pcvt/loadfont/loadfont.c
new file mode 100644
index 0000000..acdd013
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 1992, 1995 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 Brian Dunford-Shore
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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
+ * Hellmuth Michaelis and Brian Dunford-Shore
+ * 4. The name 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 ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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.
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * load a font into VGA character font memory
+ * ------------------------------------------
+ *
+ * Last Edit-Date: [Mon Mar 27 17:09:25 2000];
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <machine/pcvt_ioctl.h>
+
+#define FONT8X8 2048 /* filesize for 8x8 font */
+#define HEIGHT8X8 8 /* 8 scan lines char cell height */
+#define SSCAN8X8 143 /* 400 scan lines on screen - 256 - 1 */
+
+#define FONT8X10 2560 /* filesize for 8x10 font */
+#define HEIGHT8X10 10 /* 10 scan lines char cell height */
+#define SSCAN8X10 143 /* 400 scan lines on screen - 256 - 1 */
+
+#define FONT8X14 3584 /* filesize for 8x14 font */
+#define HEIGHT8X14 14 /* 14 scan lines char cell height */
+#define SSCAN8X14 135 /* 392 scan lines on screen - 256 - 1 */
+
+#define FONT8X16 4096 /* filesize for 8x16 font */
+#define HEIGHT8X16 16 /* 16 scan lines char cell height */
+#define SSCAN8X16 143 /* 400 scan lines on screen - 256 - 1 */
+
+struct screeninfo screeninfo;
+
+#define DEFAULTFD 0
+int fd;
+
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ FILE *in;
+ struct stat sbuf, *sbp;
+ unsigned char *fonttab;
+ int ret;
+ int chr_height;
+ int scr_scan;
+ int scr_rows;
+ int c;
+ int chr_set = -1;
+ char *filename;
+ int fflag = -1;
+ int info = -1;
+ int dflag = 0;
+ char *device;
+
+ while( (c = getopt(argc, argv, "c:d:f:i")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ chr_set = atoi(optarg);
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'f':
+ filename = optarg;
+ fflag = 1;
+ break;
+
+ case 'i':
+ info = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(chr_set == -1 || fflag == -1)
+ info = 1;
+
+ if(dflag)
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ err(1, "ERROR opening %s", device);
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(info == 1)
+ {
+ int i;
+
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+
+ switch(screeninfo.adaptor_type)
+ {
+ case UNKNOWN_ADAPTOR:
+ case MDA_ADAPTOR:
+ case CGA_ADAPTOR:
+ printf("Adaptor does not support Downloadable Fonts!\n");
+ break;
+ case EGA_ADAPTOR:
+ printheader();
+ for(i = 0;i < 4;i++)
+ {
+ printvgafontattr(i);
+ }
+ break;
+ case VGA_ADAPTOR:
+ printheader();
+ for(i = 0;i < 8;i++)
+ {
+ printvgafontattr(i);
+ }
+ }
+ printf("\n");
+ exit(0);
+ }
+
+ if(chr_set < 0 || chr_set > 7)
+ usage();
+
+ sbp = &sbuf;
+
+ if((in = fopen(filename, "r")) == NULL)
+ err(1, "cannot open file %s for reading", filename);
+
+ if((fstat(fileno(in), sbp)) != 0)
+ err(1, "cannot fstat file %s", filename);
+
+ switch(sbp->st_size)
+ {
+ case FONT8X8:
+ chr_height = HEIGHT8X8;
+ scr_scan = SSCAN8X8;
+ scr_rows = SIZ_50ROWS;
+ break;
+
+ case FONT8X10:
+ chr_height = HEIGHT8X10;
+ scr_scan = SSCAN8X10;
+ scr_rows = SIZ_40ROWS;
+ break;
+
+ case FONT8X14:
+ chr_height = HEIGHT8X14;
+ scr_scan = SSCAN8X14;
+ scr_rows = SIZ_28ROWS;
+ break;
+
+ case FONT8X16:
+ chr_height = HEIGHT8X16;
+ scr_scan = SSCAN8X16;
+ scr_rows = SIZ_25ROWS;
+ break;
+
+ default:
+ errx(1, "error, file %s is no valid font file, size=%d", argv[1], sbp->st_size);
+ }
+
+ if((fonttab = (unsigned char *)malloc((size_t)sbp->st_size)) == NULL)
+ errx(1, "error, malloc failed");
+
+ if((ret = fread(fonttab, sizeof(*fonttab), sbp->st_size, in)) != sbp->st_size)
+ errx(1, "error reading file %s, size = %d, read = is no valid font file, size=%d",
+ argv[1], sbp->st_size, ret);
+
+ loadfont(chr_set, chr_height, fonttab);
+ setfont(chr_set, 1, chr_height - 1, scr_scan, scr_rows);
+
+ exit(0);
+}
+
+setfont(charset, fontloaded, charscan, scrscan, scrrow)
+int charset, fontloaded, charscan, scrscan, scrrow;
+{
+ struct vgafontattr vfattr;
+
+ vfattr.character_set = charset;
+ vfattr.font_loaded = fontloaded;
+ vfattr.character_scanlines = charscan;
+ vfattr.screen_scanlines = scrscan;
+ vfattr.screen_size = scrrow;
+
+ if(ioctl(fd, VGASETFONTATTR, &vfattr) == -1)
+ err(1, "ioctl VGASETFONTATTR failed, error");
+}
+
+loadfont(fontset,charscanlines,font_table)
+int fontset;
+int charscanlines;
+unsigned char *font_table;
+{
+ int i, j;
+ struct vgaloadchar vlc;
+
+ vlc.character_set = fontset;
+ vlc.character_scanlines = charscanlines;
+
+ for(i = 0; i < 256; i++)
+ {
+ vlc.character = i;
+ for (j = 0; j < charscanlines; j++)
+ {
+ vlc.char_table[j] = font_table[j];
+ }
+ font_table += charscanlines;
+ if(ioctl(fd, VGALOADCHAR, &vlc) == -1)
+ err(1, "ioctl VGALOADCHAR failed, error");
+ }
+}
+
+printvgafontattr(charset)
+int charset;
+{
+ struct vgafontattr vfattr;
+ static int sizetab[] = { 25, 28, 35, 40, 43, 50 };
+
+ vfattr.character_set = charset;
+
+ if(ioctl(fd, VGAGETFONTATTR, &vfattr) == -1)
+ err(1, "ioctl VGAGETFONTATTR failed, error");
+ printf(" %d ",charset);
+ if(vfattr.font_loaded)
+ {
+
+ printf("Loaded ");
+ printf(" %2.2d ", sizetab[vfattr.screen_size]);
+ printf(" %2.2d ",
+ (((int)vfattr.character_scanlines) & 0x1f) + 1);
+ printf(" %3.3d",
+ ((int)vfattr.screen_scanlines+0x101));
+ }
+ else
+ {
+ printf("Empty");
+ }
+ printf("\n");
+}
+
+printheader()
+{
+ printf("\nEGA/VGA Charactersets Status Info:\n\n");
+ printf("Set Status Lines CharScanLines ScreenScanLines\n");
+ printf("--- ------ ----- ------------- ---------------\n");
+}
+
+usage()
+{
+ fprintf(stderr,"\nloadfont - load font into ega/vga font ram for pcvt video driver\n");
+ fprintf(stderr,"usage: loadfont -c <cset> -d <dev> -f <name> -i\n");
+ fprintf(stderr," -c <cset> characterset to load (ega 0..3, vga 0..7)\n");
+ fprintf(stderr," -d <dev> specify device\n");
+ fprintf(stderr," -f <name> filename containing binary font data\n");
+ fprintf(stderr," -i print status and types of loaded fonts (default)\n");
+ exit(1);
+}
diff --git a/usr.sbin/pcvt/scon/Makefile b/usr.sbin/pcvt/scon/Makefile
new file mode 100644
index 0000000..dc7d02a
--- /dev/null
+++ b/usr.sbin/pcvt/scon/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= scon
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/scon/scon.1 b/usr.sbin/pcvt/scon/scon.1
new file mode 100644
index 0000000..76a4c0f
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.1
@@ -0,0 +1,220 @@
+.\" Copyright (c) 1992, 2000 Hellmuth Michaelis
+.\" Copyright (c) 1992, 1994 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 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.
+.\"
+.\" Last Edit-Date: [Mon Mar 27 17:17:50 2000]
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 27, 2000
+.Dt SCON 1
+.Os
+.Sh NAME
+.Nm scon
+.Nd controls screen modes for pcvt video driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl c Ar screenno
+.Op Fl d Ar device
+.Op Fl f Ar on|off
+.Op Fl h
+.Op Fl H
+.Op Fl l
+.Op Fl m
+.Op Fl v
+.Op Fl V
+.Op Fl s Ar lines
+.Nm
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar entry,red,green,blue
+.Nm
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar default
+.Nm
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar list
+.Nm
+.Op Fl v
+.Fl t Ar timeout
+.Nm
+.Op Fl v
+.Fl 1 | Fl 8
+.Sh DESCRIPTION
+The
+.Nm
+utility controls several aspects of the runtime behaviour of the pcvt vt220
+driver.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Returns a string describing the video adaptor found by pcvt, the string
+returned could be MDA, HGC, CGA, EGA, VGA or UNKNOWN.
+.It Fl c
+Specify the screen number the current (displayed) screen should be switched
+to.
+.It Fl d
+Specify the device filename (i.e. /dev/ttyv2) further operations specified on
+the command line should be applied to.
+.It Fl f
+Some programs which silently assume 24 lines when they run on a VT220 show
+incorrect behaviour when the terminal has really 25 lines.
+To support full
+VT220 behaviour, it is possible to force pcvt to select only 24 lines when
+it is running in 25-lines pure VT mode and/or in 28-lines HP-mode.
+The
+.Fl f
+option requires one additional parameter, the string 'on' or 'off' to switch
+this mode for a virtual screen on or off respectively.
+This mode has no effect
+if any other vertical resolutions are selected than the two above mentioned.
+.It Fl h
+Prints a usage/help text.
+.It Fl l
+Lists the current configuration of runtime changeable options and fixed
+parameters (such as the type of the adaptor, and in case of a VGA adaptor,
+the Manufacturer, Chipset and 132 column support) of the output portion
+of the pcvt driver.
+.It Fl m
+Returns a string describing the connected display monitor type found by pcvt,
+the string returned can be MONO, COLOR or UNKNOWN.
+.It Fl v
+Specify verbose operation of the program.
+.It Fl V
+Switch the specified/current screen into a pure VT220 mode without recognizing
+any HP escape sequences and without displaying function key labels.
+.It Fl H
+Switch the specified/current screen into a mixed HP/VT220 mode.
+That is, that
+in addition to the full VT220 emulation, the HP function key labels and the
+escape sequences for handling the labels are available to the user.
+.It Fl s
+Specify the number of character lines on the screen.
+Possible parameters are
+25, 28, 35, 40, 43 or 50. To use all this screen sizes, the fonts required
+for proper operation of a desired size have to be downloaded to the EGA/VGA
+font ram.
+This option is available only for EGA and VGA boards.
+.It Fl p
+Modify VGA palette
+(DAC).
+The
+.Fl p
+is mutually exclusive with
+.Fl s ,
+.Fl H ,
+and
+.Fl V .
+Naturally, option
+.Fl p
+is available only for VGA boards.
+Three flavors are available.
+.Pp
+If used with argument
+.Dq Ar default ,
+this flag will restore the default palette
+(as installed by VGA ROM BIOS after hardware reset).
+.Pp
+If used with argument
+.Dq Ar list ,
+the current VGA DAC palette entries are listed.
+Each entry contains
+the table index, values for red, green, and blue, and if there's a
+known name for this entry, the color name.
+Trailing empty table
+slots (RGB values all zero) are omitted.
+.Pp
+Otherwise, four comma-separated arguments are expected.
+The first
+denotes the number of palette entry to be modified.
+This may be either
+a number between 0 and 255, or the usual name of an associated color
+(case-insensitive).
+The following values for red, green and blue are restricted to 0 through 63
+due to VGA DAC conventions.
+Note that the first delimiter within such an argument may be a colon
+.Dq \&:
+instead of a comma
+.Dq \&,
+for better readability, but this violates common command argument
+conventions.
+Multiple
+.Fl p
+options may be specified if unambiguous.
+.It Fl t
+Specifying
+.Fl t
+will activate the screen saver.
+The behaviour depends on
+.Ar timeout :
+if
+.Ar timeout
+is given as 0, the screen saver is turned off.
+Otherwise,
+.Ar timeout
+is taken as a number of seconds to wait until activating the
+screen saver.
+NOTE: the
+.Fl t
+option is only available if screen saver support has been compiled into
+the driver!
+.It Fl 1
+Sets 132 columns mode
+(only available on VGA adaptors).
+.It Fl 8
+Sets 80 columns mode.
+.El
+.Pp
+When switching between HP and VT mode, when switching the force 24 lines
+mode on and off, or when switching between 80 and 132 columns operation,
+the screen is cleared, the scrolling
+region is reset and the cursor is placed in the home position.
+.Sh EXAMPLES
+The command
+.Dq Li scon Fl H s Ar 28
+places the current screen into HP mode and sets the screen size to 28x80.
+.Pp
+Invoking
+.Do
+.Li scon Fl p
+.Ar lightgray,0,15,0
+.Fl p
+.Ar 0:45,45,45
+.Dc
+will result in green on gray output for normal text.
+Note that normal text color is light gray, and not white as one might expect.
+.Sh BUGS
+the
+.Fl c
+and
+.Fl d
+options collide somehow, this will change in a future release.
+.Sh SEE ALSO
+.Xr cursor 1 ,
+.Xr loadfont 1 ,
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/scon/scon.c b/usr.sbin/pcvt/scon/scon.c
new file mode 100644
index 0000000..0d77f11
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright (c) 1992, 2000 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 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 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.
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * scon - screen control utility for pcvt
+ * --------------------------------------
+ *
+ * Last Edit-Date: [Mon Mar 27 17:19:34 2000]
+ *
+ * $FreeBSD$
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+int aflag = -1;
+int lflag = -1;
+int mflag = -1;
+int current = -1;
+int pflag = -1;
+int hflag = -1;
+int res = -1;
+char *device;
+int dflag = -1;
+int vflag = 0;
+int Pflag = 0;
+int tflag = 0;
+int fflag = -1;
+int colms = 0;
+char *onoff;
+
+unsigned timeout;
+struct screeninfo screeninfo;
+
+#define NVGAPEL 256
+
+struct rgb {
+ unsigned r, g, b;
+ int dothis;
+};
+
+static struct rgb palette[NVGAPEL] = {
+ { 0x00, 0x00, 0x00, 0}, /* 0 - black */
+ { 0x00, 0x00, 0x2a, 0}, /* 1 - blue */
+ { 0x00, 0x2a, 0x00, 0}, /* 2 - green */
+ { 0x00, 0x2a, 0x2a, 0}, /* 3 - cyan */
+ { 0x2a, 0x00, 0x00, 0}, /* 4 - red */
+ { 0x2a, 0x00, 0x2a, 0}, /* 5 - magenta */
+ { 0x2a, 0x2a, 0x00, 0}, /* 6 */
+ { 0x2a, 0x2a, 0x2a, 0}, /* 7 - lightgray */
+ { 0x00, 0x00, 0x15, 0}, /* 8 */
+ { 0x00, 0x00, 0x3f, 0}, /* 9 */
+ { 0x00, 0x2a, 0x15, 0}, /* 10 */
+ { 0x00, 0x2a, 0x3f, 0}, /* 11 */
+ { 0x2a, 0x00, 0x15, 0}, /* 12 */
+ { 0x2a, 0x00, 0x3f, 0}, /* 13 */
+ { 0x2a, 0x2a, 0x15, 0}, /* 14 */
+ { 0x2a, 0x2a, 0x3f, 0}, /* 15 */
+ { 0x00, 0x15, 0x00, 0}, /* 16 */
+ { 0x00, 0x15, 0x2a, 0}, /* 17 */
+ { 0x00, 0x3f, 0x00, 0}, /* 18 */
+ { 0x00, 0x3f, 0x2a, 0}, /* 19 */
+ { 0x2a, 0x15, 0x00, 0}, /* 20 - brown */
+ { 0x2a, 0x15, 0x2a, 0}, /* 21 */
+ { 0x2a, 0x3f, 0x00, 0}, /* 22 */
+ { 0x2a, 0x3f, 0x2a, 0}, /* 23 */
+ { 0x00, 0x15, 0x15, 0}, /* 24 */
+ { 0x00, 0x15, 0x3f, 0}, /* 25 */
+ { 0x00, 0x3f, 0x15, 0}, /* 26 */
+ { 0x00, 0x3f, 0x3f, 0}, /* 27 */
+ { 0x2a, 0x15, 0x15, 0}, /* 28 */
+ { 0x2a, 0x15, 0x3f, 0}, /* 29 */
+ { 0x2a, 0x3f, 0x15, 0}, /* 30 */
+ { 0x2a, 0x3f, 0x3f, 0}, /* 31 */
+ { 0x15, 0x00, 0x00, 0}, /* 32 */
+ { 0x15, 0x00, 0x2a, 0}, /* 33 */
+ { 0x15, 0x2a, 0x00, 0}, /* 34 */
+ { 0x15, 0x2a, 0x2a, 0}, /* 35 */
+ { 0x3f, 0x00, 0x00, 0}, /* 36 */
+ { 0x3f, 0x00, 0x2a, 0}, /* 37 */
+ { 0x3f, 0x2a, 0x00, 0}, /* 38 */
+ { 0x3f, 0x2a, 0x2a, 0}, /* 39 */
+ { 0x15, 0x00, 0x15, 0}, /* 40 */
+ { 0x15, 0x00, 0x3f, 0}, /* 41 */
+ { 0x15, 0x2a, 0x15, 0}, /* 42 */
+ { 0x15, 0x2a, 0x3f, 0}, /* 43 */
+ { 0x3f, 0x00, 0x15, 0}, /* 44 */
+ { 0x3f, 0x00, 0x3f, 0}, /* 45 */
+ { 0x3f, 0x2a, 0x15, 0}, /* 46 */
+ { 0x3f, 0x2a, 0x3f, 0}, /* 47 */
+ { 0x15, 0x15, 0x00, 0}, /* 48 */
+ { 0x15, 0x15, 0x2a, 0}, /* 49 */
+ { 0x15, 0x3f, 0x00, 0}, /* 50 */
+ { 0x15, 0x3f, 0x2a, 0}, /* 51 */
+ { 0x3f, 0x15, 0x00, 0}, /* 52 */
+ { 0x3f, 0x15, 0x2a, 0}, /* 53 */
+ { 0x3f, 0x3f, 0x00, 0}, /* 54 */
+ { 0x3f, 0x3f, 0x2a, 0}, /* 55 */
+ { 0x15, 0x15, 0x15, 0}, /* 56 - darkgray */
+ { 0x15, 0x15, 0x3f, 0}, /* 57 - lightblue */
+ { 0x15, 0x3f, 0x15, 0}, /* 58 - lightgreen */
+ { 0x15, 0x3f, 0x3f, 0}, /* 59 - lightcyan */
+ { 0x3f, 0x15, 0x15, 0}, /* 60 - lightred */
+ { 0x3f, 0x15, 0x3f, 0}, /* 61 - lightmagenta */
+ { 0x3f, 0x3f, 0x15, 0}, /* 62 - yellow */
+ { 0x3f, 0x3f, 0x3f, 0}, /* 63 - white */
+ { 0x00, 0x00, 0x00, 0} /* 64 ... - empty */
+};
+
+static struct colname {
+ const char *name;
+ unsigned idx;
+} colnames[] = {
+ {"black", 0},
+ {"blue", 1},
+ {"green", 2},
+ {"cyan", 3},
+ {"red", 4},
+ {"magenta", 5},
+ {"brown", 20},
+ {"lightgray", 7},
+ {"lightgrey", 7},
+ {"darkgray", 56},
+ {"darkgrey", 56},
+ {"lightblue", 57},
+ {"lightgreen", 58},
+ {"lightcyan", 59},
+ {"lightred", 60},
+ {"lightmagenta", 61},
+ {"yellow", 62},
+ {"white", 63},
+ /* must be terminator: */ {(const char *)NULL, 0}
+};
+
+
+static void parsepopt(char *arg, unsigned *idx,
+ unsigned *r, unsigned *g, unsigned *b);
+static void printpalette(int fd);
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int fd;
+
+ while( (c = getopt(argc, argv, "ac:d:f:HVlms:t:vp:18")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ aflag = 1;
+ break;
+
+ case 'l':
+ lflag = 1;
+ break;
+
+ case 'm':
+ mflag = 1;
+ break;
+
+ case 'c':
+ current = atoi(optarg);
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'f':
+ onoff = optarg;
+ fflag = 1;
+ break;
+
+ case 'V':
+ pflag = 1;
+ break;
+
+ case 'H':
+ hflag = 1;
+ break;
+
+ case 's':
+ if (!strncmp(optarg, "25", 2))
+ res = SIZ_25ROWS;
+ else if(!strncmp(optarg, "28", 2))
+ res = SIZ_28ROWS;
+ else if(!strncmp(optarg, "35", 2))
+ res = SIZ_35ROWS;
+ else if(!strncmp(optarg, "40", 2))
+ res = SIZ_40ROWS;
+ else if(!strncmp(optarg, "43", 2))
+ res = SIZ_43ROWS;
+ else if(!strncmp(optarg, "50", 2))
+ res = SIZ_50ROWS;
+ break;
+
+ case 'v':
+ vflag++;
+ break;
+
+ case 'p':
+ if(!strcmp(optarg, "list"))
+ {
+ if(Pflag)
+ errx(2, "-p list is mutual exclusive with other -p options");
+ Pflag = 3;
+ }
+ else if(!strcmp(optarg, "default"))
+ {
+ if(Pflag)
+ errx(2, "multiple -p default not allowed");
+ Pflag = 2;
+ } else {
+ unsigned idx, r, g, b;
+
+ if(Pflag > 1)
+ errx(2, "-p default and -p i,r,g,b ambiguous");
+ Pflag = 1;
+ parsepopt(optarg, &idx, &r, &g, &b);
+ if(idx >= NVGAPEL)
+ errx(2, "index %u in -p option out of range", idx);
+ palette[idx].r = r;
+ palette[idx].g = g;
+ palette[idx].b = b;
+ palette[idx].dothis = 1;
+ }
+ break;
+
+ case 't':
+ tflag++;
+ timeout = atoi(optarg);
+ break;
+
+ case '1':
+ colms = 132;
+ break;
+
+ case '8':
+ colms = 80;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if((pflag == 1) && (hflag == 1))
+ usage();
+
+ if(dflag == -1 && lflag == -1 && current == -1 && pflag == -1 &&
+ hflag == -1 && res == -1 && Pflag == 0 && tflag == 0 && fflag == -1
+ && colms == 0 && mflag == -1)
+ {
+ lflag = 1;
+ }
+
+ if(dflag == -1)
+ {
+ if(vflag)
+ printf("using current device\n");
+ fd = DEFAULTFD; /* -hm, Feb 12 1993 */
+ }
+ else
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ err(1, "ERROR opening %s", device);
+ if(vflag)
+ printf("using device %s\n",device);
+ }
+
+ if(aflag == 1) /* return adaptor type */
+ {
+ printadaptor(fd);
+ exit(0);
+ }
+
+ if(mflag == 1) /* return monitor type */
+ {
+ printmonitor(fd);
+ exit(0);
+ }
+
+ if(lflag == 1) /* list information */
+ {
+ if(vflag)
+ printf("processing option -l, listing screen info\n");
+ printinfo(fd);
+ exit(0);
+ }
+
+ if(tflag) /* set screen saver timeout */
+ {
+ if(vflag)
+ {
+ printf(
+ "processing option -t, setting screen saver timeout: "
+ );
+ if(timeout)
+ printf("new timeout = %d s\n", timeout);
+ else
+ printf("turned off\n");
+ }
+
+ if(ioctl(fd, VGASCREENSAVER, &timeout) < 0)
+ {
+ warn("ioctl(VGASCREENSAVER)");
+ fprintf(stderr, "Check the driver, the screensaver is probably not compiled in!\n");
+ exit(2);
+ }
+ goto success;
+ }
+
+ if(colms)
+ {
+ if(vflag)
+ printf("Setting number of columns to %d\n", colms);
+ if(ioctl(fd, VGASETCOLMS, &colms) < 0)
+ err(2, "ioctl(VGASETCOLMS)");
+ goto success;
+ }
+
+ if(Pflag == 3)
+ {
+ /* listing VGA palette */
+ if(vflag)
+ printf("processing option -p list, "
+ "listing VGA palette\n");
+
+ printpalette(fd);
+ goto success;
+ }
+
+ if(Pflag)
+ {
+ unsigned int idx;
+
+ /* setting VGA palette */
+ if(vflag)
+ printf("processing option -p, setting VGA palette%s\n",
+ Pflag == 2? " to default": "");
+
+ for(idx = 0; idx < NVGAPEL; idx++)
+ if(Pflag == 2 || palette[idx].dothis)
+ {
+ struct vgapel p;
+ p.idx = idx;
+ p.r = palette[idx].r;
+ p.g = palette[idx].g;
+ p.b = palette[idx].b;
+ if(ioctl(fd, VGAWRITEPEL, (caddr_t)&p) < 0)
+ err(2, "ioctl(fd, VGAWRITEPEL)");
+ }
+ goto success;
+ }
+
+ screeninfo.screen_no = -1; /* We are using fd */
+ screeninfo.current_screen = current;
+ screeninfo.pure_vt_mode = -1;
+ screeninfo.screen_size = res;
+ screeninfo.force_24lines = -1;
+
+ if(current != -1) /* set current screen */
+ {
+ if(vflag)
+ printf("processing option -c, setting current screen to %d\n",current);
+
+ if(ioctl(1, VGASETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGASETSCREEN failed");
+ exit(0);
+ }
+
+ if(pflag == 1)
+ {
+ if(vflag)
+ printf("processing option -V, setting emulation to pure VT220\n");
+ screeninfo.pure_vt_mode = M_PUREVT;
+ }
+ else if(hflag == 1)
+ {
+ if(vflag)
+ printf("processing option -H, setting emulation to VT220 + HP Labels\n");
+ screeninfo.pure_vt_mode = M_HPVT;
+ }
+ else
+ {
+ if(vflag)
+ printf("no change in terminal emulation\n");
+ }
+
+ if(vflag)
+ {
+ if(res == -1)
+ printf("no change in screen resolution\n");
+ else if(res == SIZ_25ROWS)
+ printf("change screen resolution to 25 lines\n");
+ else if(res == SIZ_28ROWS)
+ printf("change screen resolution to 28 lines\n");
+ else if(res == SIZ_35ROWS)
+ printf("change screen resolution to 35 lines\n");
+ else if(res == SIZ_40ROWS)
+ printf("change screen resolution to 40 lines\n");
+ else if(res == SIZ_43ROWS)
+ printf("change screen resolution to 43 lines\n");
+ else if(res == SIZ_50ROWS)
+ printf("change screen resolution to 50 lines\n");
+ }
+
+ if(fflag == 1) /* force 24 lines on/off */
+ {
+ if(!strcmp(onoff, "on"))
+ {
+ fflag = 1;
+ }
+ else if(!strcmp(onoff, "off"))
+ {
+ fflag = 0;
+ }
+ else
+ {
+ fprintf(stderr,"you must specify 'on' or 'off' with -f option!\n");
+ exit(1);
+ }
+ }
+ screeninfo.force_24lines = fflag;
+
+ if(ioctl(fd, VGASETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGASETSCREEN failed");
+success:
+ if(vflag)
+ printf("successful execution of ioctl VGASETSCREEN!\n");
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\nscon - screen control utility for the pcvt video driver\n");
+ fprintf(stderr,"usage: scon -a -l -m -v -c [n] -d [dev] -f [on|off] -V -H -s [n]\n");
+ fprintf(stderr,"usage: scon -p [default | list | i,r,g,b] | -t [sec] | -1 | -8\n");
+ fprintf(stderr," -a list video adaptor type (MDA,CGA,EGA or VGA)\n");
+ fprintf(stderr," -c <screen no> switch current virtual screen to <screen no>\n");
+ fprintf(stderr," -d <device> set parameters(-V|-H|-s) for virtual device\n");
+ fprintf(stderr," -f <on|off> force 24 lines in VT 25 lines and HP 28 lines mode\n");
+ fprintf(stderr," -H set VT220/HP emulation mode for a virtual screen\n");
+ fprintf(stderr," -l list current parameters for a virtual screen\n");
+ fprintf(stderr," -m report monitor type (MONO/COLOR)\n");
+ fprintf(stderr," -p default set default VGA palette\n");
+ fprintf(stderr," -p list list current VGA palette\n");
+ fprintf(stderr," -p <i,r,g,b> set VGA palette entry i to r/g/b\n");
+ fprintf(stderr," -p <name,r,g,b> set VGA palette entry for color name to r/g/b\n");
+ fprintf(stderr," -s <lines> set 25, 28, 35, 40, 43 or 50 lines for a virtual screen\n");
+ fprintf(stderr," -t <timeout> set screen saver timeout [seconds]\n");
+ fprintf(stderr," -1 set 132 columns mode\n");
+ fprintf(stderr," -8 set 80 columns mode\n");
+ fprintf(stderr," -v verbose mode\n");
+ fprintf(stderr," -V set pure VT220 emulation for a virtual screen\n");
+ fprintf(stderr," -? display help (this message)\n\n");
+ exit(1);
+}
+
+printadaptor(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+ switch(screeninfo.adaptor_type)
+ {
+ default:
+ case UNKNOWN_ADAPTOR:
+ printf("UNKNOWN\n");
+ break;
+
+ case MDA_ADAPTOR:
+ printf("MDA\n");
+ break;
+
+ case CGA_ADAPTOR:
+ printf("CGA\n");
+ break;
+
+ case EGA_ADAPTOR:
+ printf("EGA\n");
+ break;
+
+ case VGA_ADAPTOR:
+ printf("VGA\n");
+ break;
+ }
+}
+
+printmonitor(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+ switch(screeninfo.monitor_type)
+ {
+ default:
+ printf("UNKNOWN\n");
+ break;
+
+ case MONITOR_MONO:
+ printf("MONO\n");
+ break;
+
+ case MONITOR_COLOR:
+ printf("COLOR\n");
+ break;
+ }
+}
+
+char *vga_type(int number)
+{
+ static char *vga_tab[] = {
+ "Generic VGA",
+ "ET4000",
+ "ET3000",
+ "PVGA1A",
+ "WD90C00",
+ "WD90C10",
+ "WD90C11",
+ "VIDEO 7 VEGA",
+ "VIDEO 7 FAST",
+ "VIDEO 7 VER5",
+ "VIDEO 7 1024I",
+ "Unknown VIDEO 7",
+ "TVGA 8800BR",
+ "TVGA 8800CS",
+ "TVGA 8900B",
+ "TVGA 8900C",
+ "TVGA 8900CL",
+ "TVGA 9000",
+ "TVGA 9100",
+ "TVGA 9200",
+ "Unknown TRIDENT",
+ "S3 80C911",
+ "S3 80C924",
+ "S3 80C801/80C805",
+ "S3 80C928",
+ "Unknown S3",
+ "CL-GD5402",
+ "CL-GD5402r1",
+ "CL-GD5420",
+ "CL-GD5420r1",
+ "CL-GD5422",
+ "CL-GD5424",
+ "CL-GD5426",
+ "CL-GD5428",
+
+ };
+ return(vga_tab[number]);
+}
+
+char *vga_family(int number)
+{
+ static char *vga_tab[] = {
+ "Generic VGA",
+ "Tseng Labs",
+ "Western Digital",
+ "Video Seven",
+ "Trident",
+ "S3 Incorporated",
+ "Cirrus Logic",
+ };
+ return(vga_tab[number]);
+}
+
+printinfo(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ err(1, "ioctl VGAGETSCREEN failed");
+
+ printf( "\nVideo Adaptor Type = ");
+
+ switch(screeninfo.adaptor_type)
+ {
+ default:
+ case UNKNOWN_ADAPTOR:
+ printf("UNKNOWN Video Adaptor\n");
+ break;
+
+ case MDA_ADAPTOR:
+ printf("MDA - Monochrome Display Adaptor\n");
+ break;
+
+ case CGA_ADAPTOR:
+ printf("CGA - Color Graphics Adaptor\n");
+ break;
+
+ case EGA_ADAPTOR:
+ printf("EGA - Enhanced Graphics Adaptor\n");
+ break;
+
+ case VGA_ADAPTOR:
+ printf("VGA - Video Graphics Adaptor/Array\n");
+ printf(" VGA Chipset Manufacturer = %s\n",
+ vga_family(screeninfo.vga_family));
+ printf(" VGA Chipset Type = %s\n",
+ vga_type(screeninfo.vga_type));
+ printf(" Support for 132 Column Mode = %s\n",
+ screeninfo.vga_132 ? "Yes" : "No");
+ break;
+ }
+
+ printf( "Display Monitor Type = ");
+
+ switch(screeninfo.monitor_type)
+ {
+ default:
+ printf("UNKNOWN Monitor Type\n");
+ break;
+
+ case MONITOR_MONO:
+ printf("Monochrome Monitor\n");
+ break;
+
+ case MONITOR_COLOR:
+ printf("Color Monitor\n");
+ break;
+ }
+
+ printf( "Number of Downloadable Fonts = %d\n",screeninfo.totalfonts);
+ printf( "Number of Virtual Screens = %d\n",screeninfo.totalscreens);
+ printf( "Info Request Screen Number = %d\n",screeninfo.screen_no);
+ printf( "Current Displayed Screen = %d\n",screeninfo.current_screen);
+
+ if(screeninfo.pure_vt_mode == M_PUREVT)
+ printf( "Terminal Emulation Mode = VT220\n");
+ else
+ printf( "Terminal Emulation Mode = VT220 with HP Features\n");
+
+ printf( "Lines = ");
+
+ switch(screeninfo.screen_size)
+ {
+ case SIZ_25ROWS:
+ printf( "25\n");
+ break;
+
+ case SIZ_28ROWS:
+ printf( "28\n");
+ break;
+
+ case SIZ_35ROWS:
+ printf( "35\n");
+ break;
+
+ case SIZ_40ROWS:
+ printf( "40\n");
+ break;
+
+ case SIZ_43ROWS:
+ printf( "43\n");
+ break;
+
+ case SIZ_50ROWS:
+ printf( "50\n");
+ break;
+
+ default:
+ printf( "UNKNOWN\n");
+ break;
+ }
+ printf( "Force 24 Lines = %s",
+ screeninfo.force_24lines ? "Yes" : "No");
+
+ printf("\n\n");
+}
+
+static const char *findname(unsigned idx)
+{
+ /* try to find a name for palette entry idx */
+ /* if multiple names exist, returns first matching */
+ register struct colname *cnp;
+
+ for(cnp = colnames; cnp->name; cnp++)
+ if(cnp->idx == idx)
+ return cnp->name;
+
+ /* not found */
+ return (const char *)NULL;
+}
+
+static void printpalette(int fd)
+{
+ register unsigned idx, last;
+
+ for(idx = 0; idx < NVGAPEL; idx++)
+ {
+ struct vgapel p;
+ p.idx = idx;
+ if(ioctl(fd, VGAREADPEL, &p) < 0)
+ err(2, "ioctl(VGAREADPEL)");
+ palette[idx].r = p.r;
+ palette[idx].g = p.g;
+ palette[idx].b = p.b;
+ }
+
+ /* find last non-empty entry */
+ for(last = NVGAPEL - 1; last; last--)
+ if(palette[last].r || palette[last].g || palette[last].b)
+ break;
+
+ if(last != NVGAPEL - 1)
+ last++;
+
+ /* now, everything's collected. print out table */
+ printf("VGA palette status\n");
+ printf("index red green blue name\n");
+ for(idx = 0; idx < last; idx++)
+ {
+ const char *cp;
+ printf("%5d %5d %5d %5d",
+ idx, palette[idx].r, palette[idx].g, palette[idx].b);
+ if(cp = findname(idx))
+ printf(" %s\n", cp);
+ else
+ putchar('\n');
+ }
+ putchar('\n');
+}
+
+
+static void parsepopt(char *arg, unsigned *idx,
+ unsigned *r, unsigned *g, unsigned *b)
+{
+ char firstarg[21];
+ register unsigned i;
+
+ if(sscanf(arg, "%20[a-zA-Z0-9]%*[,:]%u,%u,%u", firstarg, r, g, b) < 4
+ || strlen(firstarg) == 0)
+ errx(2, "too few args in -p i,r,g,b");
+
+ if(firstarg[0] >= '0' && firstarg[0] <= '9') {
+ *idx = strtoul(firstarg, NULL, 10);
+ return;
+ }
+
+ for(i = 0; colnames[i].name; i++)
+ if(strcasecmp(colnames[i].name, firstarg) == 0) {
+ *idx = colnames[i].idx;
+ return;
+ }
+ errx(2, "arg ``%s'' in -p option not recognized", firstarg);
+}
diff --git a/usr.sbin/pcvt/userkeys/Makefile b/usr.sbin/pcvt/userkeys/Makefile
new file mode 100644
index 0000000..66fe022
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+PROG= vt220keys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/userkeys/vt220keys.1 b/usr.sbin/pcvt/userkeys/vt220keys.1
new file mode 100644
index 0000000..3a4e231
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.1
@@ -0,0 +1,175 @@
+.\" $FreeBSD$
+.\"
+.Dd January 16, 2001
+.Dt VT220KEYS 1
+.Os
+.Sh NAME
+.Nm vt220keys
+.Nd "define SHIFTED function keys on VT220 terminal"
+.Sh SYNOPSIS
+.Nm
+.Op Fl cil
+.Op Ar keyname keystring ...
+.Sh DESCRIPTION
+The
+.Nm
+utility sets up a
+.Dq "vt220 terminal"
+in vt200 mode to allow user
+definition of the SHIFTED function keys.
+Each
+.Ar keyname
+specified on the command line will be loaded with
+the corresponding
+.Ar keystring .
+A
+.Ar keyname
+is one of the following words:
+.Cm F6 F7 F8 F9 F10 F11 ESC
+.Cm F12 BS F13 LF F14 HELP
+.Cm DO F17 F18 F19 F20 .
+.Ar Keystrings
+must be quoted if spaces, tabs, or shell metacharacters are included.
+.Pp
+The
+.Nm
+utility expects to receive some combination of option flags and/or
+argument pair(s), otherwise a usage message
+is printed.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl c
+Clears all SHIFTED function key definitions before setting them to user
+defined strings.
+.It Fl i
+Read the initialization file
+.Pa $HOME/.vt220rc
+for SHIFTED function key definitions.
+This is done before any
+argument pair specified on the command line is processed.
+Each line in the file must consist of two fields (separated by spaces
+or tabs) where the first field is the
+.Ar keyname
+and the second field is the
+.Ar keystring .
+The second field extends to the end of the line, thus a
+.Ar keystring
+may include spaces or tabs.
+A newline (return) may be specified
+within the string by using the C Language notation for newline (\\n).
+.It Fl l
+Locks the function keys from further definition.
+Locking occurs after processing the initialization file (if the
+.Fl i
+option is specified) and any argument
+pairs.
+The only way
+to unlock is by turning the power off.
+.El
+.Sh EXAMPLES
+.Bd -literal
+vt220keys -ci
+vt220keys F6 'nroff -ms '
+vt220keys -i F20 'cc -O -c '
+vt220keys -l HELP man
+.Ed
+.Sh "OTHER FEATURES"
+Pressing the function keys without using the shift key, generates
+a string of characters.
+With
+.Xr csh 1
+this string can be aliased to some command.
+For example:
+.Pp
+.Dl alias\ ^[[17~\ "ls\ -CR\ |\ more"
+.Pp
+where
+.Ql "^[[17~"
+is what is generated by pressing the F6 key.
+Therefore
+F6 can perform two commands, depending if pressed with/without the SHIFT
+key.
+.Pp
+The
+.Nm
+utility can be called from your
+.Pa .login
+or
+.Pa .profile
+file.
+Typically an user
+will create an initialization file and include a line like
+.Pp
+.Dl "vt220keys -ci"
+OR
+.Dl "vt220keys -cil"
+.Pp
+in the above mentioned files.
+This way the SHIFTED function keys
+will be set to your favorite commands when logging in.
+.Sh CAVEATS
+If the SHIFTED function keys are unlocked, redefinition of a SHIFTED
+function key will rewrite the old string.
+.Pp
+There are 256 bytes available for the SHIFTED function keys.
+Space is
+supplied on a first\-come/first\-serve basis.
+After the 256 bytes are
+used, you can't define any more keys unless space is cleared.
+This
+can be done by redefining a key to contain a string of fewer bytes.
+.Pp
+All key definitions are stored in volatile RAM, and are lost when
+terminal power is lost.
+.Pp
+The ESC key (unshifted) no longer generates the proper escape character.
+This
+is of particular importance since many editors require use of the
+ESC key.
+Here are some available alternatives:
+.Bl -bullet
+.It
+The escape character can be generated by typing
+.Ql ^[
+(control\-[).
+.It
+Use
+.Nm
+as follows (note
+.Ql ^[
+is control\-[)
+.Pp
+.Dl "vt220keys ESC '^['"
+.Pp
+This will require you
+to press the SHIFT key and ESC to generate the escape sequence.
+.It
+Some editors, allow other character(s) to be substituted for the
+escape character.
+For example with
+.Xr emacs 1
+include this line in your
+.Pa .emacs_pro :
+.Pp
+.Dl (bind-to-key\ "ESC-prefix"\ "\\033[23~")
+.Pp
+Thus when the ESC key is pressed,
+.Nm emacs
+will allow the characters generated
+.Pq Li ^[[23~
+to perform the same function as the escape
+character.
+.El
+.Sh FILES
+.Bl -tag -width $HOME/.vt220rc
+.It Pa $HOME/.vt220rc
+initialization file
+.El
+.Sh SEE ALSO
+.Rs
+.%B "VT220 Programmer Reference Manual"
+.Re
+.Rs
+.%B "VT220 Programmer Pocket Guide"
+.Re
diff --git a/usr.sbin/pcvt/userkeys/vt220keys.c b/usr.sbin/pcvt/userkeys/vt220keys.c
new file mode 100644
index 0000000..e3fe90c
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.c
@@ -0,0 +1,287 @@
+/*
+ * Trivial program to load VT220 Function keys with strings,
+ * note that the values only get sent when the key is shifted
+ * (shoulda been an option to flip the shift set like the Z19!)
+ *
+ * Typing no args gives help, basically pairs of keyname/value
+ * strings.
+ *
+ * Author, Author: Barry Shein, Boston University
+ *
+ * HISTORY
+ {1} 30-Oct-85 Kenneth J. Lester (ken) at ektools
+
+ Added the necessary code to read an initialization file. This
+ should make it easier to used this program. Also added code
+ that will set-up the terminal in vt200 (this saves the user the
+ trouble of checking if the set-up is in vt200).
+
+ Restructed the main function to use getopt, for argument
+ processing.
+
+ Alterated usage function to include new "i" option (init file)
+
+
+ -hm minor modifications for pcvt 2.0 release
+
+$FreeBSD$
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+/*
+ * The default toupper() macro is stupid, will toupper anything
+ */
+
+#ifdef toupper
+#undef toupper
+#endif
+#define toupper(c) (islower(c) ? ((c)-' ') : c)
+
+#define VT200_7BIT 1
+#define ESC 033
+#define INITFILE ".vt220rc"
+
+struct keynames {
+ char *name ;
+ char *string ;
+} keys[] = {
+ "F6", "17",
+ "F7", "18",
+ "F8", "19",
+ "F9", "20",
+ "F10", "21",
+ "F11", "23",
+ "ESC", "23",
+ "F12", "24",
+ "BS", "24",
+ "F13", "25",
+ "LF", "25",
+ "F14", "26",
+ "HELP", "28",
+ "DO", "29",
+ "F17", "31",
+ "F18", "32",
+ "F19", "33",
+ "F20", "34",
+ NULL, NULL
+};
+
+char prog[BUFSIZ];
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int option; /* option character returned by getopt */
+ int initf = 0; /* read initialization file */
+ int lockf = 0; /* lock keys after loading strings */
+ int clearf = 0; /* clear all keys before loading strings */
+
+ strlcpy(prog, *argv, sizeof(prog)); /* store program name */
+
+ if(argc == 1) usage(); /* program requires options */
+
+ /* get options */
+ while ((option = getopt(argc, argv, "cli")) != -1)
+ switch(option)
+ {
+ case 'c' :
+ clearf++;
+ break;
+ case 'l' :
+ lockf++;
+ break;
+ case 'i' :
+ initf++;
+ break;
+ case '?' :
+ usage();
+ }
+
+ if (VT200_7BIT)
+ printf("\033[62;1\"p"); /* vt200 7 bits */
+ else
+ printf("\033[62;2\"p"); /* vt200 8 bits */
+
+ if(clearf) clearkeys();
+
+ if (initf) getinit();
+
+ /* process {key, key string} pairs. Note optind is index to argv
+ for first pair. By adding 1 to optind insures that a pair exists
+ i.e. the last key has a key string. */
+
+ while(optind + 1 < argc)
+ {
+ dokey(argv[optind], argv[optind+1]);
+ optind += 2;
+ }
+
+ if(lockf) lockkeys();
+
+ exit(0);
+}
+
+/****************************************************************************/
+
+/*
+ * Load the VT220 SHIFT-FNKEY value, the basic pattern is
+ * "\EP1;1|"+KEYNAME+"/"+VAL_AS_HEX+"\E\\"
+ * that is, literally what is in quotes (w/o quotes) then the
+ * name of the key from the keytable above (a numeric string)
+ * then a slash, then the string value as hex pairs then ESC-BACKSLASH
+ *
+ * Note: you can gang together key defns with semicolons but that
+ * would complicate things, especially error handling, so do it all
+ * for each pair, who cares, really.
+ */
+
+dokey(nm,val) char *nm, *val;
+{
+ register char *scr;
+ register struct keynames *kp;
+
+ for(scr = nm; *scr = toupper(*scr); scr++)
+ ;
+ for(kp = keys; kp->name != NULL; kp++)
+ if(strcmp(nm,kp->name) == 0) {
+ printf("%cP1;1|%s/",ESC,kp->string);
+ while(*val) printf("%02x",*val++);
+ printf("%c\\",ESC);
+ fflush(stdout);
+ return;
+ }
+ fprintf(stderr,"Bad key name: %s\n",nm);
+ usage(); /* bad key name, give up */
+}
+
+/****************************************************************************/
+
+clearkeys()
+{
+ printf("%cP0;1|%c\\",ESC,ESC);
+ fflush(stdout);
+}
+
+/****************************************************************************/
+
+lockkeys()
+{
+ printf("%cP1;0|%c\\",ESC,ESC);
+ fflush(stdout);
+}
+
+/****************************************************************************/
+
+usage()
+{
+ int i;
+
+ fprintf(stderr,"usage: %s [-cil] [keyname string keyname string...]\n\n",prog);
+ fprintf(stderr,"The following options are available\n");
+ fprintf(stderr,"\t-c\tclears keys first\n");
+ fprintf(stderr,"\t-l\t[sets then] locks further setting\n");
+ fprintf(stderr,"\t-i\tfirst read initialization file $HOME/%s\n",INITFILE);
+ fprintf(stderr,"(note that the only way to unlock is via Set-Up)\n\n");
+ fprintf(stderr,"Keyname is one of:\n\t");
+ for(i=0; keys[i].name != NULL; i++)
+ fprintf(stderr,"%s ",keys[i].name);
+ fprintf(stderr,"\nKeyname is SHIFTED function key that sends the string\n\n");
+ fprintf(stderr,"Strings may need quoting to protect from shell\n");
+ fprintf(stderr,"You must specify an option or key,string pairs\n\n");
+ exit(1);
+}
+
+/****************************************************************************/
+
+/* This routine process the INITFILE. This file expects lines in the format
+
+ <ws> keyname ws string
+
+ Where ws is white space (spaces or tabs) and <ws> is optional white space.
+ The string may include spaces or tabs and need not be quoted. If the
+ string has the sequence of "\n" then a newline character is included in
+ the string.
+
+ examples:
+
+ F6 ls -lg\n
+ F7 uulog -s
+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+getinit()
+{
+ char *home; /* user's home directory */
+ char path[BUFSIZ]; /* full path name of init file */
+ char buf[BUFSIZ]; /* buffer to hold 1 line from init file */
+ char key[BUFSIZ]; /* buffer, to hold specified fcn key */
+ char keystr[BUFSIZ]; /* string associated with fcn key */
+ char *ptr; /* pointer to transverse buf */
+ int i, j; /* array indices */
+ int statflag; /* whether init file is regular & readable */
+ struct stat statbuf; /* stat of the init file */
+ FILE *fp; /* file pointer to init file */
+
+ /* construct full path name for init file */
+ home = getenv("HOME");
+ snprintf(path, sizeof(path), "%s/%s", home, INITFILE);
+
+ /* check status if init file */
+ if (stat(path, &statbuf) != -1)
+ {
+ statflag = statbuf.st_mode & S_IFREG && statbuf.st_mode & S_IREAD;
+ if (!statflag || (fp = fopen(path, "r")) == NULL)
+ {
+ fprintf(stderr, "couldn't open initalization file: %s\n", path);
+ exit(1);
+ }
+
+ /* process lines from init file */
+ while (fgets(buf, BUFSIZ, fp) != NULL)
+ {
+ /* variable initializations */
+ i = 0; j = 0;
+ key[0] = '\0'; keystr[0] = '\0';
+ ptr = buf;
+
+ while (*ptr == ' ' || *ptr == '\t') ptr++; /*skip whitespace*/
+
+ if (*ptr == '\n') break; /* we hit an emtpy line */
+
+ while (!isspace(*ptr) && *ptr != '\0') /* get keyname */
+ key[i++] = *ptr++;
+ key[i] = '\0'; /* place EOS in buffer */
+
+ while (*ptr == ' ' || *ptr == '\t') ptr++; /*skip whitespace*/
+
+ while (*ptr != '\n' && *ptr != '\0') /* get string */
+ {
+ /* check if string is to include newline i.e. \n */
+ if (*ptr == '\\' && *(ptr+1) == 'n')
+ {
+ keystr[j] = '\012';
+ ptr++;
+ }
+ else
+ keystr[j] = *ptr;
+ j++; ptr++;
+ }
+ keystr[j] = '\0'; /* place EOS in buffer */
+ dokey(key, keystr); /* load key with string */
+ }
+ }
+ else
+ {
+ fprintf(stderr, "init file %s not found\n\n", path);
+ usage();
+ }
+}
diff --git a/usr.sbin/pcvt/vgaio/CAUTION b/usr.sbin/pcvt/vgaio/CAUTION
new file mode 100644
index 0000000..e1eba06
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/CAUTION
@@ -0,0 +1,28 @@
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
+ THE USE OF THIS PROGRAM MAY DESTROY YOUR MONITOR !!!
+ ====================================================
+
+ IF YOU DON'T KNOW WHAT YOU ARE DOING, STAY AWAY FROM IT !!!
+ ===========================================================
+
+ 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.
+
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
diff --git a/usr.sbin/pcvt/vgaio/Makefile b/usr.sbin/pcvt/vgaio/Makefile
new file mode 100644
index 0000000..0ac0388
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+PROG= vgaio
+MAN= vgaio.8
+SRCS= vgaio.y lex.l y.tab.h
+
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR}
+
+YACC= yacc
+
+#YFLAGS+= -yd # Bison
+#YFLAGS+= -v # verbose
+
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "vgaio is not installed automatically ...."
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/vgaio/lex.l b/usr.sbin/pcvt/vgaio/lex.l
new file mode 100644
index 0000000..e729850
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/lex.l
@@ -0,0 +1,82 @@
+%{
+/*
+ * Copyright (c) 1994 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.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: lex.l,v $
+ * Revision 1.1 1994/03/29 02:47:20 mycroft
+ * pcvt 3.0, with some performance enhancements by Joerg Wunsch and me.
+ *
+ * Revision 1.2 1994/01/08 17:42:58 j
+ * cleanup
+ * made multiple commands per line work
+ * wrote man page
+ *
+ * Revision 1.3 21.12.1994 -hm
+ * Added mi command for accessing the misc out register
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "vgaio.h"
+#include "y.tab.h"
+
+extern YYSTYPE yylval;
+
+%}
+
+D [0-9a-fA-F]
+
+%%
+
+cr|CR { return CR; }
+
+ar|AR { return AR; }
+
+gr|GR { return GR; }
+
+sr|SR { return SR; }
+
+mi|MI { return MI; }
+
+{D}({D}*) { sscanf(yytext, "%x", &yylval.num); return NUM; }
+
+[ \t] { /* ignore */ }
+
+\n { return NEWLINE; }
+
+. { return yytext[0]; }
diff --git a/usr.sbin/pcvt/vgaio/vgaio.8 b/usr.sbin/pcvt/vgaio/vgaio.8
new file mode 100644
index 0000000..e7f3b24
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.8
@@ -0,0 +1,141 @@
+.\"
+.\" Copyright (c) 1994 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$
+.\" -hm updated 31.12.94
+.\"
+.Dd December 31, 1994
+.Dt VGAIO 8
+.Os
+.Sh NAME
+.Nm vgaio
+.Nd perform input/output on a Video Graphics Array
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Sh DESCRIPTION
+.Ss Purpose
+The
+.Nm
+utility
+is used to perform register-level input/output on a Video Graphics Array.
+Since some of the sequences required to access those registers are very
+silly,
+.Nm
+cares of all the things necessary and allows the user to access the
+registers of several register groups with their symbolic names.
+.Ss Options
+.Bl -tag -width 10n -offset indent
+.It Fl d
+Turn on the grammar parser debugger.
+.El
+.Ss Command language
+The command language of
+.Nm
+constitutes of some very simple tokens and rules.
+Commands are executed
+line by line as they are entered.
+Each line may contain any number of
+semicolon-separated input/output commands.
+.Pp
+Symbolic register names look like:
+.Pp
+.D1 Ao Em reggroup Ac Ns Aq Em regnumber
+.Pp
+with
+.Aq Em regnumber
+being any hexadecimal number
+(without a leading
+.Em 0x ) ,
+and
+.Aq Em reggroup
+one of the strings
+.Dq Em ar ,
+.Dq Em cr ,
+.Dq Em gr ,
+.Dq Em mi ,
+or
+.Dq Em sr ,
+standing for the
+.Em Attribute controller ,
+.Em CRT controller ,
+.Em Graphics controller ,
+.Em Miscellaneous Output Register ,
+or
+.Em Timing sequencer ,
+respectively.
+.Pp
+An input instruction has the form
+.Pp
+.D1 Ao Em regname Ac \&?
+.Pp
+and will cause
+.Nm
+to output a line like
+.Bd -ragged -offset indent
+.Aq Em regname
+.No = 0x Ns Aq Em number
+.Ed
+.Pp
+An output instruction looks like
+.Bd -ragged -offset indent
+.Ao Em regname Ac =
+.Aq Em number
+.Ed
+.Pp
+Spaces or Tabs between the
+.Aq Em reggroup ,
+the
+.Aq Em regnumber ,
+or any of the other tokens are ignored.
+They are not required anyway.
+.Pp
+The
+.Dq Em mi
+needs a single unused argument to satisfy the syntax :-) (-hm).
+.Ss Access control
+The caller must have uid 0 in order to gain the required access to
+the IO registers.
+.Sh HISTORY
+This program is considered
+.Dq hackware .
+It has been developed in order to simplify the process of developing other
+software that needs to program the Video Graphics Array.
+.Pp
+Remember, to use this program, your kernel has to be compiled with XSERVER
+being defined!
+.Sh AUTHORS
+The program has been contributed by
+.An J\(:org Wunsch ,
+Dresden
+.Aq joerg_wunsch@uriah.sax.de .
diff --git a/usr.sbin/pcvt/vgaio/vgaio.h b/usr.sbin/pcvt/vgaio/vgaio.h
new file mode 100644
index 0000000..e313f86
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1994 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.
+ */
+
+/*
+ * $Header
+ * The author may be reached unter <joerg_wunsch@uriah.sax.de>
+ *
+ * $Log: vgaio.h,v $
+ * Revision 1.1 1994/03/29 02:47:25 mycroft
+ * pcvt 3.0, with some performance enhancements by Joerg Wunsch and me.
+ *
+ * Revision 1.2 1994/01/08 17:42:58 j
+ * cleanup
+ * made multiple commands per line work
+ * wrote man page
+ *
+ *
+ */
+
+/* common structure to hold the definition for a VGA register */
+
+#ifndef VGAIO_H
+#define VGAIO_H
+
+struct reg {
+ int group, num;
+};
+
+#endif /* VGAIO_H */
diff --git a/usr.sbin/pcvt/vgaio/vgaio.y b/usr.sbin/pcvt/vgaio/vgaio.y
new file mode 100644
index 0000000..a73314a
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.y
@@ -0,0 +1,252 @@
+%{
+/*
+ * Copyright (c) 1994 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.
+ */
+
+#ident "$FreeBSD$"
+
+/*
+ * $Log: vgaio.y,v $
+ * Revision 1.1 1994/03/29 02:47:27 mycroft
+ * pcvt 3.0, with some performance enhancements by Joerg Wunsch and me.
+ *
+ * Revision 1.2 1994/01/08 17:42:58 j
+ * cleanup
+ * made multiple commands per line work
+ * wrote man page
+ *
+ * Revision 1.3 21.12.1994 -hm
+ * Added mi command for accessing the misc out register
+ * hex values shown as 2 fixed chars, added binary output
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/fcntl.h>
+#include <machine/cpufunc.h>
+#include <machine/pcvt_ioctl.h>
+
+#ifdef __NetBSD__
+#include <machine/pio.h>
+#endif
+
+#include "vgaio.h"
+
+void setreg(struct reg r, int val);
+void getreg(struct reg r);
+void yyerror(const char *msg);
+
+#define YYDEBUG 1
+
+unsigned short vgabase;
+
+%}
+
+%union {
+ int num;
+ struct reg reg;
+}
+
+%token MI GR CR SR AR NEWLINE
+%token <num> NUM
+
+%type <num> reggroup
+%type <reg> register
+
+%%
+
+interpret: lines ;
+
+lines: line
+ | lines line
+ ;
+
+line: statements NEWLINE
+ | NEWLINE
+ | error NEWLINE { fprintf(stderr, "bing!\n"); }
+ ;
+
+statements: statement
+ | statements ';' statement
+ ;
+
+statement: register '?' { getreg($1); }
+ | register '=' NUM { setreg($1, $3); }
+ | /* lambda */
+ ;
+
+register: reggroup NUM { $$.num = $2; $$.group = $1; }
+ ;
+
+reggroup: GR { $$ = GR; }
+ | CR { $$ = CR; }
+ | SR { $$ = SR; }
+ | AR { $$ = AR; }
+ | MI { $$ = MI; }
+ ;
+
+%%
+
+static struct {
+ int id;
+ const char *name;
+} regnames[] = {
+ {GR, "gr"}, {CR, "cr"}, {SR, "sr"}, {AR, "ar"}, {MI, "mi"},
+ {0, 0}
+};
+
+const char *getname(struct reg r) {
+ int idx;
+ for(idx = 0; regnames[idx].id; idx++)
+ if(regnames[idx].id == r.group)
+ return regnames[idx].name;
+ return "??";
+}
+
+/*---------------------------------------------------------------------------*
+ * return ptr to string of 1's and 0's for value
+ *---------------------------------------------------------------------------*/
+char *
+bin_str(unsigned long val, int length)
+{
+ static char buffer[80];
+ int i = 0;
+
+ if (length > 32)
+ length = 32;
+
+ val = val << (32 - length);
+
+ while (length--)
+ {
+ if (val & 0x80000000)
+ buffer[i++] = '1';
+ else
+ buffer[i++] = '0';
+ if ((length % 4) == 0 && length)
+ buffer[i++] = '.';
+ val = val << 1;
+ }
+ return (buffer);
+}
+
+void getreg(struct reg r) {
+ int val; /* FreeBSD gcc ONLY accepts an int !! */
+
+ switch(r.group) {
+ case GR:
+ outb(0x3ce, r.num);
+ val = inb(0x3cf);
+ break;
+
+ case AR:
+ r.num &= 0x1f;
+ (void)inb(vgabase + 0x0a);
+ outb(0x3c0, r.num + 0x20);
+ val = inb(0x3c1);
+ break;
+
+ case CR:
+ outb(vgabase + 4, r.num);
+ val = inb(vgabase + 5);
+ break;
+
+ case SR:
+ outb(0x3c4, r.num);
+ val = inb(0x3c5);
+ break;
+
+ case MI:
+ val = inb(0x3cc);
+ break;
+ }
+
+ printf("%s%02x = 0x%02x = %s (bin)\n", getname(r), r.num, val, bin_str(val,8));
+}
+
+void setreg(struct reg r, int val) {
+ switch(r.group) {
+ case GR:
+ outb(0x3ce, r.num);
+ outb(0x3cf, val);
+ break;
+
+ case AR:
+ r.num &= 0x1f;
+ (void)inb(vgabase + 0x0a);
+ outb(0x3c0, r.num);
+ outb(0x3c0, val);
+ outb(0x3c0, r.num + 0x20);
+ break;
+
+ case CR:
+ outb(vgabase + 4, r.num);
+ outb(vgabase + 5, val);
+ break;
+
+ case SR:
+ outb(0x3c4, r.num);
+ outb(0x3c5, val);
+ break;
+
+ case MI:
+ outb(0x3c2, val);
+ break;
+ }
+
+ printf("%s%02x set to 0x%02x = %s (bin) now\n", getname(r), r.num, val, bin_str(val,8));
+}
+
+void yyerror(const char *msg) {
+ fprintf(stderr, "yyerror: %s\n", msg);
+}
+
+int main(int argc, char **argv) {
+ int fd;
+
+ if(argc > 1) yydebug = 1;
+
+ if((fd = open("/dev/console", O_RDONLY)) < 0)
+ fd = 0;
+
+ if(ioctl(fd, KDENABIO, 0) < 0) {
+ perror("ioctl(KDENABIO)");
+ return 1;
+ }
+ vgabase = (inb(0x3cc) & 1)? 0x3d0: 0x3b0;
+ yyparse();
+
+ (void)ioctl(fd, KDDISABIO, 0);
+ return 0;
+}
diff --git a/usr.sbin/pcvt/vttest/Makefile b/usr.sbin/pcvt/vttest/Makefile
new file mode 100644
index 0000000..f5d6e7b
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= vttest
+SRCS= main.c esc.c
+
+CFLAGS+= -DUSEMYSTTY
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/vttest/README b/usr.sbin/pcvt/vttest/README
new file mode 100644
index 0000000..ffc2e8b
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/README
@@ -0,0 +1,59 @@
+$FreeBSD$
+
+NOTES FROM THE MOD.SOURCES MODERATOR:
+I split the source up into the three separate pieces it now is.
+In doing this, I put lines like "int reading;" in a header file
+that both C modules include. If your Unix requires one of these
+to be "extern int reading;" then you will have some editing to do.
+Also note that this program uses FIONREAD, which must be implemented
+differently in SystemV, etc., and check out the setjmp() call...
+ /Rich $alz
+Oh, yeah: I also wrote the Makefile and manpage, such as they are.
+-------------------------------------------------------------------
+
+This is a program to test the compatibility (or to demonstrate the
+non-compatibility) of so-called "VT100-compatible" terminals. In
+conformance of the good old hacker traditions, the only documentation
+of this program is the source code itself. To understand it, you also
+need a copy of the original VT100 manual from DEC.
+
+Comments and bug reports: Since this is a release (via USENET) to the
+whole world, a lot of people are going to have opinions and fresh
+ideas about it. (What -- bugs in MY program? Aww...) I can't deal
+with everyone sending me a hacked version, but if you have found a
+serious bug, or ported it to VMS, do tell me. I can't promise any new
+version release, though. From this version on (1.7b) VTTEST will have
+to live its own life without its father holding its hand.
+
+My address is:
+
+Network-mail address: (mcvax,seismo)!enea!suadb!lindberg
+
+Real-world-mail address: Per Lindberg
+ QZ, Stockholm University Computing Center
+ Box 27322
+ S - 102 54 Stockholm
+ SWEDEN
+
+The original version of this program is written for the Sargasso C
+compiler for the DECsystem-10. Many thanks to all sales persons with
+quote VT100-compatible unquote terminals, who prompted me to write
+this program, and also to:
+
+-- Bo Kleve, LIDAC, Linkoping University, Sweden
+ for the portation to DECSYSTEM-20 with the Sargasso C compiler
+
+-- Johan Widen, TTDS, Royal Institute of Technology, Stockholm, Sweden
+ for the portation to various UNIX systems (incl. System III and Xenix)
+
+-- Russ Herman, AES Data Inc., Missisauga, Ont. Canada
+ for fixes and code for the VT102 test
+
+Thanx also to JMR "Gremlin" at KTH, and Goran Wallberg at QZ
+for suggestions, bug fixes, etc.
+
+This program does not have support for all the different variations
+of VT100, like VT125, VT131 nor the new VT200 series. Feel free to
+add that yourself. Happy Hacking!
+
+ /TMP
diff --git a/usr.sbin/pcvt/vttest/esc.c b/usr.sbin/pcvt/vttest/esc.c
new file mode 100644
index 0000000..21baa97
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/esc.c
@@ -0,0 +1,403 @@
+#include "header.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+println(s) char *s; {
+ printf("%s\n", s);
+}
+
+esc(s) char *s; {
+ printf("%c%s", 27, s);
+}
+
+esc2(s1, s2) char s1, s2; {
+ printf("%c%s%s", 27, s1, s2);
+}
+
+brcstr(ps, c) char *ps, c; {
+ printf("%c[%s%c", 27, ps, c);
+}
+
+brc(pn,c) int pn; char c; {
+ printf("%c[%d%c", 27, pn, c);
+}
+
+brc2(pn1, pn2 ,c) int pn1, pn2; char c; {
+ printf("%c[%d;%d%c", 27, pn1, pn2, c);
+}
+
+cub(pn) int pn; { /* Cursor Backward */
+ brc(pn,'D');
+}
+cud(pn) int pn; { /* Cursor Down */
+ brc(pn,'B');
+}
+cuf(pn) int pn; { /* Cursor Forward */
+ brc(pn,'C');
+}
+cup(pn1, pn2) int pn1, pn2; { /* Cursor Position */
+ brc2(pn1, pn2, 'H');
+}
+cuu(pn) int pn; { /* Cursor Up */
+ brc(pn,'A');
+}
+da() { /* Device Attributes */
+ brc(0,'c');
+}
+decaln() { /* Screen Alignment Display */
+ esc("#8");
+}
+decdhl(lower) int lower; { /* Double Height Line (also double width) */
+ if (lower) esc("#4");
+ else esc("#3");
+}
+decdwl() { /* Double Wide Line */
+ esc("#6");
+}
+deckpam() { /* Keypad Application Mode */
+ esc("=");
+}
+deckpnm() { /* Keypad Numeric Mode */
+ esc(">");
+}
+decll(ps) char *ps; { /* Load LEDs */
+ brcstr(ps, 'q');
+}
+decrc() { /* Restore Cursor */
+ esc("8");
+}
+decreqtparm(pn) int pn; { /* Request Terminal Parameters */
+ brc(pn,'x');
+}
+decsc() { /* Save Cursor */
+ esc("7");
+}
+decstbm(pn1, pn2) int pn1, pn2; { /* Set Top and Bottom Margins */
+ if (pn1 || pn2) brc2(pn1, pn2, 'r');
+ else esc("[r");
+ /* Good for >24-line terminals */
+}
+decswl() { /* Single With Line */
+ esc("#5");
+}
+dectst(pn) int pn; { /* Invoke Confidence Test */
+ brc2(2, pn, 'y');
+}
+dsr(pn) int pn; { /* Device Status Report */
+ brc(pn, 'n');
+}
+ed(pn) int pn; { /* Erase in Display */
+ brc(pn, 'J');
+}
+el(pn) int pn; { /* Erase in Line */
+ brc(pn,'K');
+}
+hts() { /* Horizontal Tabulation Set */
+ esc("H");
+}
+hvp(pn1, pn2) int pn1, pn2; { /* Horizontal and Vertical Position */
+ brc2(pn1, pn2, 'f');
+}
+ind() { /* Index */
+ esc("D");
+}
+nel() { /* Next Line */
+ esc("E");
+}
+ri() { /* Reverse Index */
+ esc("M");
+}
+ris() { /* Reset to Initial State */
+ esc("c");
+}
+rm(ps) char *ps; { /* Reset Mode */
+ brcstr(ps, 'l');
+}
+scs(g,c) int g; char c; { /* Select character Set */
+ printf("%c%c%c%c%c%c%c", 27, g ? ')' : '(', c,
+ 27, g ? '(' : ')', 'B',
+ g ? 14 : 15);
+}
+sgr(ps) char *ps; { /* Select Graphic Rendition */
+ brcstr(ps, 'm');
+}
+sm(ps) char *ps; { /* Set Mode */
+ brcstr(ps, 'h');
+}
+tbc(pn) int pn; { /* Tabulation Clear */
+ brc(pn, 'g');
+}
+
+vt52cup(l,c) int l,c; {
+ printf("%cY%c%c", 27, l + 31, c + 31);
+}
+
+char inchar() {
+
+ /*
+ * Wait until a character is typed on the terminal
+ * then read it, without waiting for CR.
+ */
+
+#ifdef UNIX
+ int lval, waittime, getpid(); static int val; char ch;
+
+ fflush(stdout);
+ lval = val;
+ brkrd = 0;
+ reading = 1;
+ read(STDIN_FILENO,&ch,1);
+ reading = 0;
+ if (brkrd)
+ val = 0177;
+ else
+ val = ch;
+ if ((val==0177) && (val==lval))
+ kill(getpid(), (int) SIGTERM);
+#endif
+#ifdef SARG10
+ int val, waittime;
+
+ waittime = 0;
+ while(!uuo(051,2,&val)) { /* TTCALL 2, (INCHRS) */
+ zleep(100); /* Wait 0.1 seconds */
+ if ((waittime += ttymode) > 600) /* Time-out, in case */
+ return('\177'); /* of hung in ttybin(1) */
+ }
+#endif
+#ifdef SARG20 /* try to fix a time-out function */
+ int val, waittime;
+
+ waittime = 0;
+ while(jsys(SIBE,2,_PRIIN) == 0) { /* Is input empty? */
+ zleep(100);
+ if ((waittime += ttymode) > 600)
+ return('\177');
+ }
+ ejsys(BIN,_PRIIN);
+ val = jsac[2];
+#endif
+ return(val);
+}
+
+char *instr() {
+
+ /*
+ * Get an unfinished string from the terminal:
+ * wait until a character is typed on the terminal,
+ * then read it, and all other available characters.
+ * Return a pointer to that string.
+ */
+
+
+ int i, val, crflag; long l1; char ch;
+ static char result[80];
+
+ i = 0;
+ result[i++] = inchar();
+/* Wait 0.1 seconds (1 second in vanilla UNIX) */
+#ifdef SARG10
+ if (trmop(01031,0) < 5) zleep(500); /* wait longer if low speed */
+ else zleep(100);
+#else
+ zleep(100);
+#endif
+#ifdef UNIX
+ fflush(stdout);
+#ifdef XENIX
+ while(rdchk(0)) {
+ read(STDIN_FILENO,result+i,1);
+ if (i++ == 78) break;
+ }
+#else
+#ifdef SIII
+ while(read(STDERR_FILENO,result+i,1) == 1)
+ if (i++ == 78) break;
+#else
+ while(ioctl(0,FIONREAD,&l1), l1 > 0L) {
+ while(l1-- > 0L) {
+ read(STDIN_FILENO,result+i,1);
+ if (i++ == 78) goto out1;
+ }
+ }
+out1:
+#endif
+#endif
+#endif
+#ifdef SARG10
+ while(uuo(051,2,&val)) { /* TTCALL 2, (INCHRS) */
+ if (!(val == '\012' && crflag)) /* TOPS-10 adds LF to CR */
+ result[i++] = val;
+ crflag = val == '\015';
+ if (i == 79) break;
+ zleep(50); /* Wait 0.05 seconds */
+ }
+#endif
+#ifdef SARG20
+ while(jsys(SIBE,2,_PRIIN) != 0) { /* read input until buffer is empty */
+ ejsys(BIN,_PRIIN);
+ result[i++] = jsac[2];
+ if (i == 79) break;
+ zleep(50); /* Wait 0.05 seconds */
+ }
+#endif
+ result[i] = '\0';
+ return(result);
+}
+
+ttybin(bin) int bin; {
+#ifdef SARG10
+ #define OPEN 050
+ #define IO_MOD 0000017
+ #define _IOPIM 2
+ #define _IOASC 0
+ #define _TOPAG 01021
+ #define _TOSET 01000
+
+ int v;
+ static int arglst[] = {
+ _IOPIM,
+ `TTY`,
+ 0
+ };
+ arglst[0] = bin ? _IOPIM : _IOASC;
+ v = uuo(OPEN, 1, &arglst[0]);
+ if (!v) { printf("OPEN failed"); exit(); }
+ trmop(_TOPAG + _TOSET, bin ? 0 : 1);
+ ttymode = bin;
+#endif
+#ifdef SARG20
+ /* TTYBIN will set the line in BINARY/ASCII mode
+ * BINARY mode is needed to send control characters
+ * Bit 28 must be 0 (we don't flip it).
+ * Bit 29 is used for the mode change.
+ */
+
+ #define _TTASC 0000100
+ #define _MOXOF 0000043
+
+ int v;
+
+ ejsys(RFMOD,_CTTRM);
+ v = ejsys(SFMOD,_CTTRM, bin ? (~_TTASC & jsac[2]) : (_TTASC | jsac[2]));
+ if (v) { printf("SFMOD failed"); exit(); }
+ v = ejsys(MTOPR,_CTTRM,_MOXOF,0);
+ if (v) { printf("MTOPR failed"); exit(); }
+#endif
+}
+
+#ifdef SARG20
+/*
+ * SUPERBIN turns off/on all input character interrupts
+ * This affects ^C, ^O, ^T
+ * Beware where and how you use it !!!!!!!
+ */
+
+superbin(bin) int bin; {
+ int v;
+
+ v = ejsys(STIW,(0//-5), bin ? 0 : -1);
+ if (v) { printf("STIW superbinary setting failed"); exit(); }
+ ttymode = bin;
+}
+
+/*
+ * PAGE affects the ^S/^Q handshake.
+ * Set bit 34 to turn it on. Clear it for off.
+ */
+
+page(bin) int bin; {
+ int v;
+
+ #define TT_PGM 0000002
+
+ ejsys(RFMOD,_CTTRM); /* Get the current terminal status */
+ v = ejsys(STPAR,_CTTRM, bin ? (TT_PGM | jsac[2]) : (~TT_PGM & jsac[2]));
+ if (v) { printf("STPAR failed"); exit(); }
+}
+#endif
+
+trmop(fc,arg) int fc, arg; {
+#ifdef SARG10
+ int retvalp;
+ int arglst[3];
+
+ /* TRMOP is a TOPS-10 monitor call that does things to the terminal. */
+
+ /* Find out TTY nbr (PA1050 barfs if TRMOP get -1 instead of udx) */
+ /* A TRMNO monitor call returns the udx (Universal Device Index) */
+
+ arglst[0] = fc; /* function code */
+ arglst[1] = calli(0115, -1); /* udx, TRMNO. UUO */
+ arglst[2] = arg; /* Optional argument */
+
+ if (calli(0116, 3 // &arglst[0], &retvalp)) /* TRMOP. UUO */
+ return (retvalp);
+ else {
+ printf("?Error return in TRMOP.");
+ exit();
+ }
+#endif
+}
+
+inputline(s) char *s; {
+ scanf("%s",s);
+#ifdef SARG10
+ readnl();
+#endif
+#ifdef SARG20
+ readnl();
+#endif
+}
+
+inflush() {
+
+ /*
+ * Flush input buffer, make sure no pending input character
+ */
+
+ int val;
+
+#ifdef UNIX
+#ifdef XENIX
+ while(rdchk(0)) read(STDIN_FILENO,&val,1);
+#else
+#ifdef SIII
+ while(read(STDERR_FILENO,&val,1));
+#else
+ long l1;
+ ioctl (0, FIONREAD, &l1);
+ while(l1-- > 0L) read(STDIN_FILENO,&val,1);
+#endif
+#endif
+#endif
+#ifdef SARG10
+ while(uuo(051,2,&val)) /* TTCALL 2, (INCHRS) */
+ ;
+#endif
+#ifdef SARG20
+ ejsys(CFIBF,_PRIIN); /* Clear input buffer */
+#endif
+}
+
+zleep(t) int t; {
+
+/*
+ * Sleep and do nothing (don't waste CPU) for t milliseconds
+ */
+
+#ifdef SARG10
+ calli(072,t); /* (HIBER) t milliseconds */
+#endif
+#ifdef SARG20
+ ejsys(DISMS,t); /* DISMISS for t milliseconds */
+#endif
+#ifdef UNIX
+ t = t / 1000;
+ if (t == 0) t = 1;
+ sleep(t); /* UNIX can only sleep whole seconds */
+#endif
+}
diff --git a/usr.sbin/pcvt/vttest/header.h b/usr.sbin/pcvt/vttest/header.h
new file mode 100644
index 0000000..ae1ccaf
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/header.h
@@ -0,0 +1,45 @@
+/* $FreeBSD$ */
+#define VERSION "1.7b 1985-04-19"
+
+/* Choose one of these */
+
+/* #define XENIX */ /* XENIX implies UNIX */
+/* #define SIII */ /* SIII implies UNIX, (NDELAY a la System III) */
+#define UNIX /* UNIX */
+/* #define VMS */ /* VMS not done yet -- send me your version!!!! */
+/* #define SARG20 */ /* Sargasso C for TOPS-20 */
+/* #define SARG10 */ /* Sargasso C for TOPS-10 */
+
+/* These #ifdef:s are implementation dependent stuff for the Sargasso C */
+/* Unix C barfs on directives like "#strings", so we keep them */
+/* indented. Then unix c can't find them, but Sargasso C *can*. */
+/* Admittedly kludgey, but it works...) */
+#ifdef SARG10
+ #define _UNIXCON /* Make UNIX-flavored I/O on TOPS */
+ #strings low /* put strings in lowseg mem so we can modify them. */
+#endif
+#ifdef SARG20
+ #define _UNIXCON /* Make UNIX-flavored I/O on TOPS */
+ #strings low /* put strings in lowseg mem so we can modify them. */
+ #include <TOPS20.HDR>
+#endif
+
+#include <stdio.h>
+
+
+#ifdef UNIX
+#include <ctype.h>
+#include <sgtty.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <unistd.h>
+jmp_buf intrenv;
+struct sgttyb sgttyOrg, sgttyNew;
+char stdioBuf[BUFSIZ];
+int brkrd, reading;
+extern onterm(), onbrk();
+#ifdef SIII
+#include <fcntl.h>
+#endif
+#endif
+int ttymode;
diff --git a/usr.sbin/pcvt/vttest/main.c b/usr.sbin/pcvt/vttest/main.c
new file mode 100644
index 0000000..0a6aa83
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/main.c
@@ -0,0 +1,2018 @@
+/*
+ VTTEST.C
+
+ Written Novemeber 1983 - July 1984 by Per Lindberg,
+ Stockholm University Computer Center (QZ), Sweden.
+
+ THE MAD PROGRAMMER STRIKES AGAIN!
+
+ This software is (c) 1984 by QZ
+ Non-commercial use and copying allowed.
+
+If you are developing a commercial product, and use this program to do
+it, and that product is successful, please send a sum of money of your
+choice to the address below.
+
+*/
+
+/* $FreeBSD$ */
+
+#include "header.h"
+
+char inchar(), *instr(), *lookup();
+
+struct table {
+ int key;
+ char *msg;
+} paritytable[] = {
+ { 1, "NONE" },
+ { 4, "ODD" },
+ { 5, "EVEN" },
+ { -1, "" }
+},nbitstable[] = {
+ { 1, "8" },
+ { 2, "7" },
+ { -1,"" }
+},speedtable[] = {
+ { 0, "50" },
+ { 8, "75" },
+ { 16, "110" },
+ { 24, "132.5"},
+ { 32, "150" },
+ { 40, "200" },
+ { 48, "300" },
+ { 56, "600" },
+ { 64, "1200" },
+ { 72, "1800" },
+ { 80, "2000" },
+ { 88, "2400" },
+ { 96, "3600" },
+ { 104, "4800" },
+ { 112, "9600" },
+ { 120, "19200" },
+ { -1, "" }
+};
+
+#ifdef USEMYSTTY
+#ifndef stty
+int stty(fd,ptr)
+int fd;
+struct sgttyb *ptr;
+{
+ return(ioctl(fd, TIOCSETP, ptr));
+}
+#endif
+#ifndef gtty
+int gtty(fd,ptr)
+int fd;
+struct sgttyb *ptr;
+{
+ return(ioctl(fd, TIOCGETP, ptr));
+}
+#endif
+#endif
+
+main() {
+
+ int menuchoice;
+
+ static char *mainmenu[] = {
+ "Exit",
+ "Test of cursor movements",
+ "Test of screen features",
+ "Test of character sets",
+ "Test of double-sized characters",
+ "Test of keyboard",
+ "Test of terminal reports",
+ "Test of VT52 mode",
+ "Test of VT102 features (Insert/Delete Char/Line)",
+ "Test of known bugs",
+ "Test of reset and self-test",
+ ""
+ };
+
+#ifdef UNIX
+ initterminal(setjmp(intrenv));
+ signal(SIGINT, (void *)onbrk);
+ signal(SIGTERM, (void *)onterm);
+ reading = 0;
+#else
+ initterminal(0);
+#endif
+ do {
+#ifdef SARG20
+ ttybin(1); /* set line to binary mode again. It's reset somehow!! */
+#endif
+ ed(2);
+ cup(5,10); printf("VT100 test program, version %s", VERSION);
+ cup(7,10); println("Choose test type:");
+ menuchoice = menu(mainmenu);
+ switch (menuchoice) {
+ case 1: tst_movements(); break;
+ case 2: tst_screen(); break;
+ case 3: tst_characters(); break;
+ case 4: tst_doublesize(); break;
+ case 5: tst_keyboard(); break;
+ case 6: tst_reports(); break;
+ case 7: tst_vt52(); break;
+ case 8: tst_insdel(); break;
+ case 9: tst_bugs(); break;
+ case 10: tst_rst(); break;
+ }
+ } while (menuchoice);
+ bye();
+}
+
+tst_movements() {
+
+ /* Test of:
+ CUF (Cursor Forward)
+ CUB (Cursor Backward)
+ CUD (Cursor Down) IND (Index) NEL (Next Line)
+ CUU (Cursor Up) RI (Reverse Index)
+ CUP (Cursor Position) HVP (Horizontal and Vertical Position)
+ ED (Erase in Display)
+ EL (Erase in Line)
+ DECALN (Screen Alignment Display)
+ <CR> <BS>
+ Cursor control characters inside CSI sequences
+ */
+
+ int i, row, col, pass, width, hlfxtra;
+ char c, *ctext = "This is a correct sentence";
+
+ for (pass = 0; pass <= 1; pass++) {
+ if (pass == 0) { rm("?3"); width = 80; hlfxtra = 0; }
+ else { sm("?3"); width = 132; hlfxtra = 26; }
+
+ decaln();
+ cup( 9,10+hlfxtra); ed(1);
+ cup(18,60+hlfxtra); ed(0); el(1);
+ cup( 9,71+hlfxtra); el(0);
+ for (row = 10; row <= 16; row++) {
+ cup(row, 10+hlfxtra); el(1);
+ cup(row, 71+hlfxtra); el(0);
+ }
+ cup(17,30); el(2);
+ for (col = 1; col <= width; col++) {
+ hvp(24, col); printf("*");
+ hvp( 1, col); printf("*");
+ }
+ cup(2,2);
+ for (row = 2; row <= 23; row++) {
+ printf("+");
+ cub(1);
+ ind();
+ }
+ cup(23,width-1);
+ for (row = 23; row >=2; row--) {
+ printf("+");
+ cub(1); ri();
+ }
+ cup(2,1);
+ for (row = 2; row <= 23; row++) {
+ printf("*");
+ cup(row, width);
+ printf("*");
+ cub(10);
+ if(row < 10)
+ nel();
+ else
+ printf("\n");
+ }
+ cup(2,10);
+ cub(42+hlfxtra); cuf(2);
+ for (col = 3; col <= width-2; col++) {
+ printf("+");
+ cuf(0); cub(2); cuf(1);
+ }
+ cup(23,70+hlfxtra);
+ cuf(42+hlfxtra); cub(2);
+ for (col = width-2; col >= 3; col--) {
+ printf("+");
+ cub(1); cuf(1); cub(0); printf("%c", 8);
+ }
+ cup( 1, 1); cuu(10); cuu(1); cuu(0);
+ cup(24,width); cud(10); cud(1); cud(0);
+
+ cup(10,12+hlfxtra);
+ for (row = 10; row <= 15; row++) {
+ for (col = 12+hlfxtra; col <= 69+hlfxtra; col++) printf(" ");
+ cud(1); cub(58);
+ }
+ cuu(5); cuf(1);
+ printf("The screen should be cleared, and have an unbroken bor-");
+ cup(12,13+hlfxtra);
+ printf("der of *'s and +'s around the edge, and exactly in the");
+ cup(13,13+hlfxtra);
+ printf("middle there should be a frame of E's around this text");
+ cup(14,13+hlfxtra);
+ printf("with one (1) free position around it. ");
+ holdit();
+ }
+ rm("?3");
+
+ ed(2);
+ cup(1,1);
+ println("Test of cursor-control characters inside ESC sequences.");
+ println("Below should be two identical lines:");
+ println("");
+ println("A B C D E F G H I J K L M N O P Q R S");
+ for (i = 1; i < 20; i++) {
+ printf("%c", 64 + i);
+ brcstr("2\010", 'C'); /* Two forward, one backspace */
+ }
+ println("");
+ println("");
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ println("Test of leading zeros in ESC sequences.");
+ printf("Two lines below you should see the sentence \"%s\".",ctext);
+ for (col = 1; *ctext; col++)
+ printf("\033[00000000004;00000000%dH%c",col,*ctext++);
+ cup(20,1);
+ holdit();
+}
+
+tst_screen() {
+
+ /* Test of:
+ - DECSTBM (Set Top and Bottom Margins)
+ - TBC (Tabulation Clear)
+ - HTS (Horizontal Tabulation Set)
+ - SM RM (Set/Reset mode): - 80/132 chars
+ - Origin: Realtive/absolute
+ - Scroll: Smooth/jump
+ - Wraparound
+ - SGR (Select Graphic Rendition)
+ - SM RM (Set/Reset Mode) - Inverse
+ - DECSC (Save Cursor)
+ - DECRC (Restore Cursor)
+ */
+
+ int i, j, cset, row, col, down, soft, background;
+
+ static char *tststr = "*qx`";
+ static char *attr[5] = { ";0", ";1", ";4", ";5", ";7" };
+
+ cup(1,1);
+ sm("?7"); /* Wrap Around ON */
+ for (col = 1; col <= 160; col++) printf("*");
+ rm("?7"); /* Wrap Around OFF */
+ cup(3,1);
+ for (col = 1; col <= 160; col++) printf("*");
+ sm("?7"); /* Wrap Around ON */
+ cup(5,1);
+ println("This should be three identical lines of *'s completely filling");
+ println("the top of the screen without any empty lines between.");
+ println("(Test of WRAP AROUND mode setting.)");
+ holdit();
+
+ ed(2);
+ tbc(3);
+ cup(1,1);
+ for (col = 1; col <= 78; col += 3) {
+ cuf(3); hts();
+ }
+ cup(1,4);
+ for (col = 4; col <= 78; col += 6) {
+ tbc(0); cuf(6);
+ }
+ cup(1,7); tbc(1); tbc(2); /* no-op */
+ cup(1,1); for (col = 1; col <= 78; col += 6) printf("\t*");
+ cup(2,2); for (col = 2; col <= 78; col += 6) printf(" *");
+ cup(4,1);
+ println("Test of TAB setting/resetting. These two lines");
+ printf("should look the same. ");
+ holdit();
+ for (background = 0; background <= 1; background++) {
+ if (background) rm("?5");
+ else sm("?5");
+ sm("?3"); /* 132 cols */
+ ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
+ cup(1,1); tbc(3);
+ for (col = 1; col <= 132; col += 8) {
+ cuf(8); hts();
+ }
+ cup(1,1); for (col = 1; col <= 130; col += 10) printf("1234567890");
+ printf("12");
+ for (row = 3; row <= 20; row++) {
+ cup(row,row);
+ printf("This is 132 column mode, %s background.",
+ background ? "dark" : "light");
+ }
+ holdit();
+ rm("?3"); /* 80 cols */
+ ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
+ cup(1,1); for (col = 1; col <= 80; col += 10) printf("1234567890");
+ for (row = 3; row <= 20; row++) {
+ cup(row,row);
+ printf("This is 80 column mode, %s background.",
+ background ? "dark" : "light");
+ }
+ holdit();
+ }
+
+ ed(2);
+ sm("?6"); /* Origin mode (relative) */
+ for (soft = -1; soft <= 0; soft++) {
+ if (soft) sm("?4");
+ else rm("?4");
+ for (row = 12; row >= 1; row -= 11) {
+ decstbm(row, 24-row+1);
+ ed(2);
+ for (down = 0; down >= -1; down--) {
+ if (down) cuu(24);
+ else cud(24);
+ for (i = 1; i <= 30; i++) {
+ printf("%s scroll %s region %d Line %d\n",
+ soft ? "Soft" : "Jump",
+ down ? "down" : "up",
+ 2*(13-row), i);
+ if (down) { ri(); ri(); }
+ }
+ }
+ holdit();
+ }
+ }
+ ed(2);
+ decstbm(23,24);
+ printf(
+ "\nOrigin mode test. This line should be at the bottom of the screen.");
+ cup(1,1);
+ printf("%s",
+ "This line should be the one above the bottom of the screeen. ");
+ holdit();
+ ed(2);
+ rm("?6"); /* Origin mode (absolute) */
+ cup(24,1);
+ printf(
+ "Origin mode test. This line should be at the bottom of the screen.");
+ cup(1,1);
+ printf("%s", "This line should be at the top if the screen. ");
+ holdit();
+ decstbm(1,24);
+
+ ed(2);
+ cup( 1,20); printf("Graphic rendition test pattern:");
+ cup( 4, 1); sgr("0"); printf("vanilla");
+ cup( 4,40); sgr("0;1"); printf("bold");
+ cup( 6, 6); sgr(";4"); printf("underline");
+ cup( 6,45);sgr(";1");sgr("4");printf("bold underline");
+ cup( 8, 1); sgr("0;5"); printf("blink");
+ cup( 8,40); sgr("0;5;1"); printf("bold blink");
+ cup(10, 6); sgr("0;4;5"); printf("underline blink");
+ cup(10,45); sgr("0;1;4;5"); printf("bold underline blink");
+ cup(12, 1); sgr("1;4;5;0;7"); printf("negative");
+ cup(12,40); sgr("0;1;7"); printf("bold negative");
+ cup(14, 6); sgr("0;4;7"); printf("underline negative");
+ cup(14,45); sgr("0;1;4;7"); printf("bold underline negative");
+ cup(16, 1); sgr("1;4;;5;7"); printf("blink negative");
+ cup(16,40); sgr("0;1;5;7"); printf("bold blink negative");
+ cup(18, 6); sgr("0;4;5;7"); printf("underline blink negative");
+ cup(18,45); sgr("0;1;4;5;7"); printf("bold underline blink negative");
+ sgr("");
+
+ rm("?5"); /* Inverse video off */
+ cup(23,1); el(0); printf("Dark background. "); holdit();
+ sm("?5"); /* Inverse video */
+ cup(23,1); el(0); printf("Light background. "); holdit();
+ rm("?5");
+ ed(2);
+ cup(8,12); printf("normal");
+ cup(8,24); printf("bold");
+ cup(8,36); printf("underscored");
+ cup(8,48); printf("blinking");
+ cup(8,60); printf("reversed");
+ cup(10,1); printf("stars:");
+ cup(12,1); printf("line:");
+ cup(14,1); printf("x'es:");
+ cup(16,1); printf("diamonds:");
+ for (cset = 0; cset <= 3; cset++) {
+ for (i = 0; i <= 4; i++) {
+ cup(10 + 2 * cset, 12 + 12 * i);
+ sgr(attr[i]);
+ if (cset == 0 || cset == 2) scs(0,'B');
+ else scs(0,'0');
+ for (j = 0; j <= 4; j++) {
+ printf("%c", tststr[cset]);
+ }
+ decsc();
+ cup(cset + 1, i + 1); sgr(""); scs(0,'B'); printf("A");
+ decrc();
+ for (j = 0; j <= 4; j++) {
+ printf("%c", tststr[cset]);
+ }
+ }
+ }
+ sgr("0"); scs(0,'B'); cup(21,1);
+ println("Test of the SAVE/RESTORE CURSOR feature. There should");
+ println("be ten characters of each flavour, and a rectangle");
+ println("of 5 x 4 A's filling the top left of the screen.");
+ holdit();
+}
+
+tst_characters() {
+ /* Test of:
+ SCS (Select character Set)
+ */
+
+ int i, j, g, cset;
+ char chcode[5], *setmsg[5];
+
+ chcode[0] = 'A';
+ chcode[1] = 'B';
+ chcode[2] = '0';
+ chcode[3] = '1';
+ chcode[4] = '2';
+ setmsg[0] = "UK / national";
+ setmsg[1] = "US ASCII";
+ setmsg[2] = "Special graphics and line drawing";
+ setmsg[3] = "Alternate character ROM standard characters";
+ setmsg[4] = "Alternate character ROM special graphics";
+
+ cup(1,10); printf("Selected as G0 (with SI)");
+ cup(1,48); printf("Selected as G1 (with SO)");
+ for (cset = 0; cset <= 4; cset++) {
+ scs(1,'B');
+ cup(3 + 4 * cset, 1);
+ sgr("1");
+ printf("Character set %c (%s)",chcode[cset], setmsg[cset]);
+ sgr("0");
+ for (g = 0; g <= 1; g++) {
+ scs(g,chcode[cset]);
+ for (i = 1; i <= 3; i++) {
+ cup(3 + 4 * cset + i, 10 + 38 * g);
+ for (j = 0; j <= 31; j++) {
+ printf("%c", i * 32 + j);
+ }
+ }
+ }
+ }
+ scs(1,'B');
+ cup(24,1); printf("These are the installed character sets. ");
+ holdit();
+}
+
+tst_doublesize() {
+ /* Test of:
+ DECSWL (Single Width Line)
+ DECDWL (Double Width Line)
+ DECDHL (Double Height Line) (also implicit double width)
+ */
+
+ int col, i, w, w1;
+
+ /* Print the test pattern in both 80 and 132 character width */
+
+ for(w = 0; w <= 1; w++) {
+ w1 = 13 * w;
+
+ ed(2);
+ cup(1, 1);
+ if (w) { sm("?3"); printf("132 column mode"); }
+ else { rm("?3"); printf(" 80 column mode"); }
+
+ cup( 5, 3 + 2 * w1);
+ printf("v------- left margin");
+
+ cup( 7, 3 + 2 * w1);
+ printf("This is a normal-sized line");
+ decdhl(0); decdhl(1); decdwl(); decswl();
+
+ cup( 9, 2 + w1);
+ printf("This is a Double-width line");
+ decswl(); decdhl(0); decdhl(1); decdwl();
+
+ cup(11, 2 + w1);
+ decdwl(); decswl(); decdhl(1); decdhl(0);
+ printf("This is a Double-width-and-height line");
+ cup(12, 2 + w1);
+ decdwl(); decswl(); decdhl(0); decdhl(1);
+ printf("This is a Double-width-and-height line");
+
+ cup(14, 2 + w1);
+ decdwl(); decswl(); decdhl(1); decdhl(0); el(2);
+ printf("This is another such line");
+ cup(15, 2 + w1);
+ decdwl(); decswl(); decdhl(0); decdhl(1);
+ printf("This is another such line");
+
+ cup(17, 3 + 2 * w1);
+ printf("^------- left margin");
+
+ cup(21, 1);
+ printf("This is not a double-width line");
+ for (i = 0; i <= 1; i++) {
+ cup(21,6);
+ if (i) { printf("**is**"); decdwl(); }
+ else { printf("is not"); decswl(); }
+ cup(23,1); holdit();
+ }
+ }
+ /* Set vanilla tabs for next test */
+ cup(1,1); tbc(3); for (col = 1; col <= 132; col += 8) { cuf(8); hts(); }
+ rm("?3");
+ ed(2);
+ scs(0,'0');
+
+ cup( 8,1); decdhl(0); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
+ cup( 9,1); decdhl(1); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
+ cup(10,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(11,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(12,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(13,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(14,1); decdhl(0); printf("x x");
+ cup(15,1); decdhl(1); printf("x x");
+ cup(16,1); decdhl(0); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
+ cup(17,1); decdhl(1); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
+ scs(0,'B'); sgr("1;5");
+ cup(12,3);
+ printf("* The mad programmer strikes again * ");
+ cup(13,3); printf("%c",9); cub(6);
+ printf("* The mad programmer strikes again *");
+ sgr("0");
+ cup(22,1);
+ println("Another test pattern... a frame with blinking bold text,");
+ printf("all in double-height double-width size. ");
+ holdit();
+
+ decstbm(8,24); /* Absolute origin mode, so cursor is set at (1,1) */
+ cup(8,1);
+ for (i = 1; i <= 12; i++)
+ ri();
+ decstbm(0,0); /* No scroll region */
+ cup(1,1);
+ printf("%s", "Exactly half of the box should remain. ");
+ holdit();
+}
+
+tst_keyboard() {
+
+/* Test of:
+ - DECLL (Load LEDs)
+ - Keyboard return messages
+ - SM RM (Set/Reset Mode) - Cursor Keys
+ - Auto repeat
+ - DECKPAM (Keypad Application Mode)
+ - DECKPNM (Keypad Numeric Mode)
+
+The standard VT100 keayboard layout:
+
+ UP DN LE RI
+
+ESC 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ =+ `~ BS
+
+TAB* qQ wW eE rR tT yY uU iI oO pP [{ ]} DEL
+
+** ** aA sS dD fF gG hH jJ kK lL ;: ," RETN \|
+
+** **** zZ xX cC vV bB nN mM ,< .> /? **** LF
+
+ ****************SPACE BAR****************
+
+ PF1 PF2 PF3 PF4
+
+ *7* *8* *9* *-*
+
+ *4* *5* *6* *,*
+
+ *1* *2* *3*
+
+ ***0*** *.* ENT
+*/
+
+ char *ledmsg[6], *ledseq[6];
+
+ int i, j, okflag;
+ int kblayout;
+ int ckeymode;
+ int fkeymode;
+ char kbdc;
+ char *kbds = " ";
+ char *curkeystr, *fnkeystr, *abmstr;
+ char arptstring[500];
+
+ static struct key {
+ char c;
+ int row;
+ int col;
+ char *symbol;
+ } keytab [] = {
+ { 27, 1, 0, "ESC" },
+ { '1', 1, 6, "1" }, { '!', 1, 7, "!" },
+ { '2', 1, 11, "2" }, { '@', 1, 12, "@" },
+ { '3', 1, 16, "3" }, { '#', 1, 17, "#" },
+ { '4', 1, 21, "4" }, { '$', 1, 22, "$" },
+ { '5', 1, 26, "5" }, { '%', 1, 27, "%" },
+ { '6', 1, 31, "6" }, { '^', 1, 32, "^" },
+ { '7', 1, 36, "7" }, { '&', 1, 37, "&" },
+ { '8', 1, 41, "8" }, { '*', 1, 42, "*" },
+ { '9', 1, 46, "9" }, { '(', 1, 47, "(" },
+ { '0', 1, 51, "0" }, { ')', 1, 52, ")" },
+ { '-', 1, 56, "-" }, { '_', 1, 57, "_" },
+ { '=', 1, 61, "=" }, { '+', 1, 62, "+" },
+ { '`', 1, 66, "`" }, { '~', 1, 67, "~" },
+ { 8, 1, 70, "BS" },
+ { 9, 2, 0, " TAB " },
+ { 'q', 2, 8, "q" }, { 'Q', 2, 9, "Q" },
+ { 'w', 2, 13, "w" }, { 'W', 2, 14, "W" },
+ { 'e', 2, 18, "e" }, { 'E', 2, 19, "E" },
+ { 'r', 2, 23, "r" }, { 'R', 2, 24, "R" },
+ { 't', 2, 28, "t" }, { 'T', 2, 29, "T" },
+ { 'y', 2, 33, "y" }, { 'Y', 2, 34, "Y" },
+ { 'u', 2, 38, "u" }, { 'U', 2, 39, "U" },
+ { 'i', 2, 43, "i" }, { 'I', 2, 44, "I" },
+ { 'o', 2, 48, "o" }, { 'O', 2, 49, "O" },
+ { 'p', 2, 53, "p" }, { 'P', 2, 54, "P" },
+ { '[', 2, 58, "[" }, { '{', 2, 59, "{" },
+ { ']', 2, 63, "]" }, { '}', 2, 64, "}" },
+ { 127, 2, 71, "DEL" },
+ { 'a', 3, 10, "a" }, { 'A', 3, 11, "A" },
+ { 's', 3, 15, "s" }, { 'S', 3, 16, "S" },
+ { 'd', 3, 20, "d" }, { 'D', 3, 21, "D" },
+ { 'f', 3, 25, "f" }, { 'F', 3, 26, "F" },
+ { 'g', 3, 30, "g" }, { 'G', 3, 31, "G" },
+ { 'h', 3, 35, "h" }, { 'H', 3, 36, "H" },
+ { 'j', 3, 40, "j" }, { 'J', 3, 41, "J" },
+ { 'k', 3, 45, "k" }, { 'K', 3, 46, "K" },
+ { 'l', 3, 50, "l" }, { 'L', 3, 51, "L" },
+ { ';', 3, 55, ";" }, { ':', 3, 56, ":" },
+ {'\'', 3, 60, "'" }, { '"', 3, 61,"\"" },
+ { 13, 3, 65, "RETN"},
+ {'\\', 3, 71,"\\" }, { '|', 3, 72, "|" },
+ { 'z', 4, 12, "z" }, { 'Z', 4, 13, "Z" },
+ { 'x', 4, 17, "x" }, { 'X', 4, 18, "X" },
+ { 'c', 4, 22, "c" }, { 'C', 4, 23, "C" },
+ { 'v', 4, 27, "v" }, { 'V', 4, 28, "V" },
+ { 'b', 4, 32, "b" }, { 'B', 4, 33, "B" },
+ { 'n', 4, 37, "n" }, { 'N', 4, 38, "N" },
+ { 'm', 4, 42, "m" }, { 'M', 4, 43, "M" },
+ { ',', 4, 47, "," }, { '<', 4, 48, "<" },
+ { '.', 4, 52, "." }, { '>', 4, 53, ">" },
+ { '/', 4, 57, "/" }, { '?', 4, 58, "?" },
+ { 10, 4, 69, "LF" },
+ { ' ', 5, 13, " SPACE BAR "},
+ {'\0', 0, 0, "" }
+ };
+
+ static struct natkey {
+ char natc;
+ int natrow;
+ int natcol;
+ char *natsymbol;
+ } natkeytab [][29] = {
+ {
+ { '"', 1, 12, "\""},
+ { '&', 1, 32, "&" },
+ { '/', 1, 37, "/" },
+ { '(', 1, 42, "(" },
+ { ')', 1, 47, ")" },
+ { '=', 1, 52, "=" },
+ { '+', 1, 56, "+" }, { '?', 1, 57, "?" },
+ { '`', 1, 61, "`" }, { '@', 1, 62, "@" },
+ { '<', 1, 66, "<" }, { '>', 1, 67, ">" },
+ { '}', 2, 58, "}" }, { ']', 2, 59, "]" },
+ { '^', 2, 63, "^" }, { '~', 2, 64, "~" },
+ { '|', 3, 55, "|" }, {'\\', 3, 56,"\\" },
+ { '{', 3, 60, "{" }, { '[', 3, 61, "[" },
+ {'\'', 3, 71, "'" }, { '*', 3, 72, "*" },
+ { ',', 4, 47, "," }, { ';', 4, 48, ";" },
+ { '.', 4, 52, "." }, { ':', 4, 53, ":" },
+ { '-', 4, 57, "-" }, { '_', 4, 58, "_" },
+ {'\0', 0, 0, "" }
+ },
+ {
+ { '"', 1, 12, "\""},
+ { '&', 1, 32, "&" },
+ { '/', 1, 37, "/" },
+ { '(', 1, 42, "(" },
+ { ')', 1, 47, ")" },
+ { '=', 1, 52, "=" },
+ { '+', 1, 56, "+" }, { '?', 1, 57, "?" },
+ { '`', 1, 61, "`" }, { '@', 1, 62, "@" },
+ { '<', 1, 66, "<" }, { '>', 1, 67, ">" },
+ { '}', 2, 58, "}" }, { ']', 2, 59, "]" },
+ { '~', 2, 63, "~" }, { '^', 2, 64, "^" },
+ { '|', 3, 55, "|" }, {'\\', 3, 56,"\\" },
+ { '{', 3, 60, "{" }, { '[', 3, 61, "[" },
+ {'\'', 3, 71, "'" }, { '*', 3, 72, "*" },
+ { ',', 4, 47, "," }, { ';', 4, 48, ";" },
+ { '.', 4, 52, "." }, { ':', 4, 53, ":" },
+ { '-', 4, 57, "-" }, { '_', 4, 58, "_" },
+ {'\0', 0, 0, "" }
+ }
+ };
+
+ static struct curkey {
+ char *curkeymsg[3];
+ int curkeyrow;
+ int curkeycol;
+ char *curkeysymbol;
+ char *curkeyname;
+ } curkeytab [] = {
+
+ /* A Reset, A Set, VT52 */
+
+ {{"\033[A","\033OA","\033A"}, 0, 56, "UP", "Up arrow" },
+ {{"\033[B","\033OB","\033B"}, 0, 61, "DN", "Down arrow" },
+ {{"\033[D","\033OD","\033D"}, 0, 66, "LT", "Left arrow" },
+ {{"\033[C","\033OC","\033C"}, 0, 71, "RT", "Right arrow"},
+ {{"", "", "" }, 0, 0, "", "" }
+ };
+
+ static struct fnkey {
+ char *fnkeymsg[4];
+ int fnkeyrow;
+ int fnkeycol;
+ char *fnkeysymbol;
+ char *fnkeyname;
+ } fnkeytab [] = {
+
+ /* ANSI-num,ANSI-app,VT52-nu,VT52-ap, r, c, symb name */
+
+ {{"\033OP","\033OP","\033P","\033P" }, 6, 59, "PF1", "PF1" },
+ {{"\033OQ","\033OQ","\033Q","\033Q" }, 6, 63, "PF2", "PF2" },
+ {{"\033OR","\033OR","\033R","\033R" }, 6, 67, "PF3", "PF3" },
+ {{"\033OS","\033OS","\033S","\033S" }, 6, 71, "PF4", "PF4" },
+ {{"7", "\033Ow","7", "\033?w"}, 7, 59, " 7 ", "Numeric 7" },
+ {{"8", "\033Ox","8", "\033?x"}, 7, 63, " 8 ", "Numeric 8" },
+ {{"9", "\033Oy","9", "\033?y"}, 7, 67, " 9 ", "Numeric 9" },
+ {{"-", "\033Om","-", "\033?m"}, 7, 71, " - ", "Minus" },
+ {{"4", "\033Ot","4", "\033?t"}, 8, 59, " 4 ", "Numeric 4" },
+ {{"5", "\033Ou","5", "\033?u"}, 8, 63, " 5 ", "Numeric 5" },
+ {{"6", "\033Ov","6", "\033?v"}, 8, 67, " 6 ", "Numeric 6" },
+ {{",", "\033Ol",",", "\033?l"}, 8, 71, " , ", "Comma" },
+ {{"1", "\033Oq","1", "\033?q"}, 9, 59, " 1 ", "Numeric 1" },
+ {{"2", "\033Or","2", "\033?r"}, 9, 63, " 2 ", "Numeric 2" },
+ {{"3", "\033Os","3", "\033?s"}, 9, 67, " 3 ", "Numeric 3" },
+ {{"0", "\033Op","0", "\033?p"},10, 59," O ","Numeric 0"},
+ {{".", "\033On",".", "\033?n"},10, 67, " . ", "Point" },
+ {{"\015", "\033OM","\015", "\033?M"},10, 71, "ENT", "ENTER" },
+ {{"","","",""}, 0, 0, "", "" }
+ };
+
+ static struct ckey {
+ int ccount;
+ char *csymbol;
+ } ckeytab [] = {
+ { 0, "NUL (CTRL-@ or CTRL-Space)" },
+ { 0, "SOH (CTRL-A)" },
+ { 0, "STX (CTRL-B)" },
+ { 0, "ETX (CTRL-C)" },
+ { 0, "EOT (CTRL-D)" },
+ { 0, "ENQ (CTRL-E)" },
+ { 0, "ACK (CTRL-F)" },
+ { 0, "BEL (CTRL-G)" },
+ { 0, "BS (CTRL-H) (BACK SPACE)" },
+ { 0, "HT (CTRL-I) (TAB)" },
+ { 0, "LF (CTRL-J) (LINE FEED)" },
+ { 0, "VT (CTRL-K)" },
+ { 0, "FF (CTRL-L)" },
+ { 0, "CR (CTRL-M) (RETURN)" },
+ { 0, "SO (CTRL-N)" },
+ { 0, "SI (CTRL-O)" },
+ { 0, "DLE (CTRL-P)" },
+ { 0, "DC1 (CTRL-Q) (X-On)" },
+ { 0, "DC2 (CTRL-R)" },
+ { 0, "DC3 (CTRL-S) (X-Off)" },
+ { 0, "DC4 (CTRL-T)" },
+ { 0, "NAK (CTRL-U)" },
+ { 0, "SYN (CTRL-V)" },
+ { 0, "ETB (CTRL-W)" },
+ { 0, "CAN (CTRL-X)" },
+ { 0, "EM (CTRL-Y)" },
+ { 0, "SUB (CTRL-Z)" },
+ { 0, "ESC (CTRL-[) (ESCAPE)" },
+ { 0, "FS (CTRL-\\ or CTRL-? or CTRL-_)" },
+ { 0, "GS (CTRL-])" },
+ { 0, "RS (CTRL-^ or CTRL-~ or CTRL-`)" },
+ { 0, "US (CTRL-_ or CTRL-?)" }
+ };
+
+ static char *keyboardmenu[] = {
+ "Standard American ASCII layout",
+ "Swedish national layout D47",
+ "Swedish national layout E47",
+ /* add new keyboard layouts here */
+ ""
+ };
+
+ static char *curkeymodes[3] = {
+ "ANSI / Cursor key mode RESET",
+ "ANSI / Cursor key mode SET",
+ "VT52 Mode"
+ };
+
+ static char *fnkeymodes[4] = {
+ "ANSI Numeric mode",
+ "ANSI Application mode",
+ "VT52 Numeric mode",
+ "VT52 Application mode"
+ };
+
+ ledmsg[0] = "L1 L2 L3 L4"; ledseq[0] = "1;2;3;4";
+ ledmsg[1] = " L2 L3 L4"; ledseq[1] = "1;0;4;3;2";
+ ledmsg[2] = " L2 L3"; ledseq[2] = "1;4;;2;3";
+ ledmsg[3] = "L1 L2"; ledseq[3] = ";;2;1";
+ ledmsg[4] = "L1"; ledseq[4] = "1";
+ ledmsg[5] = ""; ledseq[5] = "";
+
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ ed(2);
+ cup(10,1);
+ println("These LEDs (\"lamps\") on the keyboard should be on:");
+ for (i = 0; i <= 5; i++) {
+ cup(10,52); el(0); printf("%s", ledmsg[i]);
+ decll("0");
+ decll(ledseq[i]);
+ cup(12,1); holdit();
+ }
+
+ ed(2);
+ cup(10,1);
+ println("Test of the AUTO REPEAT feature");
+ println("");
+ println("Hold down an alphanumeric key for a while, then push RETURN.");
+ printf("%s", "Auto Repeat OFF: ");
+ rm("?8");
+ inputline(arptstring);
+ if (strlen(arptstring) == 0) println("No characters read!??");
+ else if (strlen(arptstring) == 1) println("OK.");
+ else println("Too many characters read.");
+ println("");
+ println("Hold down an alphanumeric key for a while, then push RETURN.");
+ printf("%s", "Auto Repeat ON: ");
+ sm("?8");
+ inputline(arptstring);
+ if (strlen(arptstring) == 0) println("No characters read!??");
+ else if (strlen(arptstring) == 1) println("Not enough characters read.");
+ else println("OK.");
+ println("");
+ holdit();
+
+ ed(2);
+ cup(5,10);
+ println("Choose keyboard layout:");
+ kblayout = menu(keyboardmenu);
+ if (kblayout) {
+ kblayout--;
+ for (j = 0; natkeytab[kblayout][j].natc != '\0'; j++) {
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ if (keytab[i].row == natkeytab[kblayout][j].natrow &&
+ keytab[i].col == natkeytab[kblayout][j].natcol) {
+ keytab[i].c = natkeytab[kblayout][j].natc;
+ keytab[i].symbol = natkeytab[kblayout][j].natsymbol;
+ break;
+ }
+ }
+ }
+ }
+
+ ed(2);
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ cup(1 + 2 * keytab[i].row, 1 + keytab[i].col);
+ sgr("7");
+ printf("%s", keytab[i].symbol);
+ sgr("");
+ }
+ cup(22,1);
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ sgttyNew.sg_flags &= ~ECHO;
+ stty(0, &sgttyNew);
+#endif
+ inflush();
+ printf("Press each key, both shifted and unshifted. Finish with RETURN:");
+ do { /* while (kbdc != 13) */
+ cup(23,1); kbdc = inchar();
+ cup(23,1); el(0);
+ sprintf(kbds, "%c", kbdc);
+ chrprint(kbds);
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ if (keytab[i].c == kbdc) {
+ cup(1 + 2 * keytab[i].row, 1 + keytab[i].col);
+ printf("%s", keytab[i].symbol);
+ break;
+ }
+ }
+ } while (kbdc != 13);
+#ifdef SARG10
+ inchar(); /* Local hack: Read LF that TOPS-10 adds to CR */
+#endif
+ cup(23,1); el(0);
+
+ for (ckeymode = 0; ckeymode <= 2; ckeymode++) {
+ if (ckeymode) sm("?1");
+ else rm("?1");
+ for (i = 0; curkeytab[i].curkeysymbol[0] != '\0'; i++) {
+ cup(1 + 2 * curkeytab[i].curkeyrow, 1 + curkeytab[i].curkeycol);
+ sgr("7");
+ printf("%s", curkeytab[i].curkeysymbol);
+ sgr("");
+ }
+ cup(20,1); printf("<%s>%20s", curkeymodes[ckeymode], "");
+ cup(22,1); el(0);
+ cup(22,1); printf("%s", "Press each cursor key. Finish with TAB.");
+ for(;;) {
+ cup(23,1);
+ if (ckeymode == 2) rm("?2"); /* VT52 mode */
+ curkeystr = instr();
+ esc("<"); /* ANSI mode */
+ cup(23,1); el(0);
+ cup(23,1); chrprint(curkeystr);
+ if (!strcmp(curkeystr,"\t")) break;
+ for (i = 0; curkeytab[i].curkeysymbol[0] != '\0'; i++) {
+ if (!strcmp(curkeystr,curkeytab[i].curkeymsg[ckeymode])) {
+ sgr("7");
+ printf(" (%s key) ", curkeytab[i].curkeyname);
+ sgr("");
+ cup(1 + 2 * curkeytab[i].curkeyrow,
+ 1 + curkeytab[i].curkeycol);
+ printf("%s", curkeytab[i].curkeysymbol);
+ break;
+ }
+ }
+ if (i == sizeof(curkeytab) / sizeof(struct curkey) - 1) {
+ sgr("7");
+ printf("%s", " (Unknown cursor key) ");
+ sgr("");
+ }
+ }
+ }
+
+ for (fkeymode = 0; fkeymode <= 3; fkeymode++) {
+ for (i = 0; fnkeytab[i].fnkeysymbol[0] != '\0'; i++) {
+ cup(1 + 2 * fnkeytab[i].fnkeyrow, 1 + fnkeytab[i].fnkeycol);
+ sgr("7");
+ printf("%s", fnkeytab[i].fnkeysymbol);
+ sgr("");
+ }
+ cup(20,1); printf("<%s>%20s", fnkeymodes[fkeymode], "");
+ cup(22,1); el(0);
+ cup(22,1); printf("%s", "Press each function key. Finish with TAB.");
+ for(;;) {
+ cup(23,1);
+ if (fkeymode >= 2) rm("?2"); /* VT52 mode */
+ if (fkeymode % 2) deckpam(); /* Application mode */
+ else deckpnm(); /* Numeric mode */
+ fnkeystr = instr();
+ esc("<"); /* ANSI mode */
+ cup(23,1); el(0);
+ cup(23,1); chrprint(fnkeystr);
+ if (!strcmp(fnkeystr,"\t")) break;
+ for (i = 0; fnkeytab[i].fnkeysymbol[0] != '\0'; i++) {
+ if (!strcmp(fnkeystr,fnkeytab[i].fnkeymsg[fkeymode])) {
+ sgr("7");
+ printf(" (%s key) ", fnkeytab[i].fnkeyname);
+ sgr("");
+ cup(1 + 2 * fnkeytab[i].fnkeyrow, 1 + fnkeytab[i].fnkeycol);
+ printf("%s", fnkeytab[i].fnkeysymbol);
+ break;
+ }
+ }
+ if (i == sizeof(fnkeytab) / sizeof(struct fnkey) - 1) {
+ sgr("7");
+ printf("%s", " (Unknown function key) ");
+ sgr("");
+ }
+ }
+ }
+
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ ed(2);
+ cup(5,1);
+ println("Finally, a check of the ANSWERBACK MESSAGE, which can be sent");
+ println("by pressing CTRL-BREAK. The answerback message can be loaded");
+ println("in SET-UP B by pressing SHIFT-A and typing e.g.");
+ println("");
+ println(" \" H e l l o , w o r l d Return \"");
+ println("");
+ println("(the double-quote characters included). Do that, and then try");
+ println("to send an answerback message with CTRL-BREAK. If it works,");
+ println("the answerback message should be displayed in reverse mode.");
+ println("Finish with a single RETURN.");
+
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ do {
+ cup(17,1);
+ inflush();
+ abmstr = instr();
+ cup(17,1);
+ el(0);
+ chrprint(abmstr);
+ } while (strcmp(abmstr,"\r"));
+
+ ed(2);
+ for (i = 0; i < 32; i++) {
+ cup(1 + (i % 16), 1 + 40 * (i / 16));
+ sgr("7");
+ printf("%s", ckeytab[i].csymbol);
+ sgr("0");
+ }
+ cup(19,1);
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ println(
+ "Push each CTRL-key TWICE. Note that you should be able to send *all*");
+ println(
+ "CTRL-codes twice, including CTRL-S (X-Off) and CTRL-Q (X-Off)!");
+ println(
+ "Finish with DEL (also called DELETE or RUB OUT), or wait 1 minute.");
+#ifdef UNIX
+#ifdef SIII
+ sgttyNew.sg_flags &= ~CBREAK;
+ stty(0, &sgttyNew);
+#endif
+ sgttyNew.sg_flags |= RAW;
+ stty(0, &sgttyNew);
+#endif
+ ttybin(1);
+#ifdef SARG20
+ page(0); /* Turn off all character processing at input */
+ superbin(1); /* Turn off ^C (among others). Keep your fingers crossed!! */
+#endif
+ do {
+ cup(23,1); kbdc = inchar();
+ cup(23,1); el(0);
+ if (kbdc < 32) printf(" %s", ckeytab[kbdc].csymbol);
+ else {
+ sprintf(kbds, "%c", kbdc);
+ chrprint(kbds);
+ printf("%s", " -- not a CTRL key");
+ }
+ if (kbdc < 32) ckeytab[kbdc].ccount++;
+ if (ckeytab[kbdc].ccount == 2) {
+ cup(1 + (kbdc % 16), 1 + 40 * (kbdc / 16));
+ printf("%s", ckeytab[kbdc].csymbol);
+ }
+ } while (kbdc != '\177');
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~RAW;
+ sgttyNew.sg_flags |= ECHO;
+ stty(0, &sgttyNew);
+#ifdef SIII
+ sgttyNew.sg_flags |= CBREAK;
+ stty(0, &sgttyNew);
+#endif
+#endif
+ ttybin(0);
+#ifdef SARG20
+ superbin(0); /* Puuuh! We made it!? */
+ page(1); /* Back to normal input processing */
+ ttybin(1); /* This must be the mode for DEC20 */
+#endif
+ cup(24,1);
+ okflag = 1;
+ for (i = 0; i < 32; i++) if (ckeytab[i].ccount < 2) okflag = 0;
+ if (okflag) printf("%s", "OK. ");
+ else printf("%s", "You have not been able to send all CTRL keys! ");
+ holdit();
+}
+
+tst_reports() {
+ /* Test of:
+ <ENQ> (AnswerBack Message)
+ SM RM (Set/Reset Mode) - LineFeed / Newline
+ DSR (Device Status Report)
+ DA (Device Attributes)
+ DECREQTPARM (Request Terminal Parameters)
+ */
+
+ int parity, nbits, xspeed, rspeed, clkmul, flags;
+ int i, reportpos;
+ char *report, *report2;
+ static char *attributes[][2] = {
+ { "\033[?1;0c", "No options (vanilla VT100)" },
+ { "\033[?1;1c", "VT100 with STP" },
+ { "\033[?1;2c", "VT100 with AVO (could be a VT102)" },
+ { "\033[?1;3c", "VT100 with STP and AVO" },
+ { "\033[?1;4c", "VT100 with GPO" },
+ { "\033[?1;5c", "VT100 with STP and GPO" },
+ { "\033[?1;6c", "VT100 with AVO and GPO" },
+ { "\033[?1;7c", "VT100 with STP, AVO and GPO" },
+ { "\033[?1;11c", "VT100 with PP and AVO" },
+ { "\033[?1;15c", "VT100 with PP, GPO and AVO" },
+ { "\033[?4;2c", "VT132 with AVO" },
+ { "\033[?4;3c", "VT132 with AVO and STP" },
+ { "\033[?4;6c", "VT132 with GPO and AVO" },
+ { "\033[?4;7c", "VT132 with GPO, AVO, and STP" },
+ { "\033[?4;11c", "VT132 with PP and AVO" },
+ { "\033[?4;15c", "VT132 with PP, GPO and AVO" },
+ { "\033[?7c", "VT131" },
+ { "\033[?12;5c", "VT125" }, /* VT125 also has ROM version */
+ { "\033[?12;7c", "VT125 with AVO" }, /* number, so this won't work */
+ { "\033[?5;0c", "VK100 (GIGI)" },
+ { "\033[?5c", "VK100 (GIGI)" },
+ { "", "" }
+ };
+
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~ECHO;
+ stty(0, &sgttyNew);
+#endif
+ cup(5,1);
+ println("This is a test of the ANSWERBACK MESSAGE. (To load the A.B.M.");
+ println("see the TEST KEYBOARD part of this program). Below here, the");
+ println("current answerback message in your terminal should be");
+ println("displayed. Finish this test with RETURN.");
+ cup(10,1);
+ inflush();
+ printf("%c", 5); /* ENQ */
+ report = instr();
+ cup(10,1);
+ chrprint(report);
+ cup(12,1);
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ println("Test of LineFeed/NewLine mode.");
+ cup(3,1);
+ sm("20");
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ printf("NewLine mode set. Push the RETURN key: ");
+ report = instr();
+ cup(4,1);
+ el(0);
+ chrprint(report);
+ if (!strcmp(report, "\015\012")) printf(" -- OK");
+ else printf(" -- Not expected");
+ cup(6,1);
+ rm("20");
+ printf("NewLine mode reset. Push the RETURN key: ");
+ report = instr();
+ cup(7,1);
+ el(0);
+ chrprint(report);
+ if (!strcmp(report, "\015")) printf(" -- OK");
+ else printf(" -- Not expected");
+ cup(9,1);
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ printf("Test of Device Status Report 5 (report terminal status).");
+ cup(2,1);
+ dsr(5);
+ report = instr();
+ cup(2,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (!strcmp(report,"\033[0n")) printf(" -- means \"TERMINAL OK\"");
+ else if (!strcmp(report,"\033[3n")) printf(" -- means \"TERMINAL OK\"");
+ else printf(" -- Unknown response!");
+
+ cup(4,1);
+ println("Test of Device Status Report 6 (report cursor position).");
+ cup(5,1);
+ dsr(6);
+ report = instr();
+ cup(5,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (!strcmp(report,"\033[5;1R")) printf(" -- OK");
+ else printf(" -- Unknown response!");
+
+ cup(7,1);
+ println("Test of Device Attributes report (what are you)");
+ cup(8,1);
+ da(0);
+ report = instr();
+ cup(8,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ for (i = 0; *attributes[i][0] != '\0'; i++) {
+ if (!strcmp(report,attributes[i][0])) break;
+ }
+ if (*attributes[i][0] == '\0')
+ printf(" -- Unknown response, refer to the manual");
+ else {
+ printf(" -- means %s", attributes[i][1]);
+ if (i) {
+ cup(9,1);
+ println("Legend: STP = Processor Option");
+ println(" AVO = Advanced Video Option");
+ println(" GPO = Graphics Processor Option");
+ println(" PP = Printer Port");
+ }
+ }
+
+ cup(14,1);
+ println("Test of the \"Request Terminal Parameters\" feature, argument 0.");
+ cup(15,1);
+ decreqtparm(0);
+ report = instr();
+ cup(15,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (strlen(report) < 16
+ || report[0] != '\033'
+ || report[1] != '['
+ || report[2] != '2'
+ || report[3] != ';')
+ println(" -- Bad format");
+ else {
+ reportpos = 4;
+ parity = scanto(report, &reportpos, ';');
+ nbits = scanto(report, &reportpos, ';');
+ xspeed = scanto(report, &reportpos, ';');
+ rspeed = scanto(report, &reportpos, ';');
+ clkmul = scanto(report, &reportpos, ';');
+ flags = scanto(report, &reportpos, 'x');
+ if (parity == 0 || nbits == 0 || clkmul == 0) println(" -- Bad format");
+ else println(" -- OK");
+ printf(
+ "This means: Parity %s, %s bits, xmitspeed %s, recvspeed %s.\n",
+ lookup(paritytable, parity),
+ lookup(nbitstable, nbits),
+ lookup(speedtable, xspeed),
+ lookup(speedtable, rspeed));
+ printf("(CLoCk MULtiplier = %d, STP option flags = %d)\n", clkmul, flags);
+ }
+
+ cup(19,1);
+ println("Test of the \"Request Terminal Parameters\" feature, argument 1.");
+ cup(20,1);
+ decreqtparm(1); /* Does the same as decreqtparm(0), reports "3" */
+ report2 = instr();
+ cup(20,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report2);
+ if (strlen(report2) < 3
+ || report2[2] != '3')
+ println(" -- Bad format");
+ else {
+ report2[2] = '2';
+ if (!strcmp(report,report2)) println(" -- OK");
+ else println(" -- Bad format");
+ }
+ cup(24,1);
+ holdit();
+#ifdef UNIX
+ sgttyNew.sg_flags |= ECHO;
+ stty(0, &sgttyNew);
+#endif
+}
+
+tst_vt52() {
+
+ static struct rtabl {
+ char *rcode;
+ char *rmsg;
+ } resptable[] = {
+ { "\033/K", " -- OK (means Standard VT52)" },
+ { "\033/Z", " -- OK (means VT100 emulating VT52)" },
+ { "", " -- Unknown response"}
+ };
+
+ int i,j;
+ char *response;
+
+ rm("?2"); /* Reset ANSI (VT100) mode, Set VT52 mode */
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ esc("H"); /* Cursor home */
+ for (i = 0; i <= 23; i++) {
+ for (j = 0; j <= 9; j++)
+ printf("%s", "FooBar ");
+ println("Bletch");
+ }
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+
+ vt52cup(7,47);
+ printf("nothing more.");
+ for (i = 1; i <= 10; i++) printf("THIS SHOULD GO AWAY! ");
+ for (i = 1; i <= 5; i++) {
+ vt52cup(1,1);
+ printf("%s", "Back scroll (this should go away)");
+ esc("I"); /* Reverse LineFeed (with backscroll!) */
+ }
+ vt52cup(12,60);
+ esc("J"); /* Erase to end of screen */
+ for (i = 2; i <= 6; i++) {
+ vt52cup(i,1);
+ esc("K"); /* Erase to end of line */
+ }
+
+ for (i = 2; i <= 23; i++) {
+ vt52cup(i,70); printf("%s", "**Foobar");
+ }
+ vt52cup(23,10);
+ for (i = 23; i >= 2; i--) {
+ printf("%s", "*");
+ printf("%c", 8); /* BS */
+ esc("I"); /* Reverse LineFeed (LineStarve) */
+ }
+ vt52cup(1,70);
+ for (i = 70; i >= 10; i--) {
+ printf("%s", "*");
+ esc("D"); esc("D"); /* Cursor Left */
+ }
+ vt52cup(24,10);
+ for (i = 10; i <= 70; i++) {
+ printf("%s", "*");
+ printf("%c", 8); /* BS */
+ esc("C"); /* Cursor Right */
+ }
+ vt52cup(2,11);
+ for (i = 2; i <= 23; i++) {
+ printf("%s", "!");
+ printf("%c", 8); /* BS */
+ esc("B"); /* Cursor Down */
+ }
+ vt52cup(23,69);
+ for (i = 23; i >= 2; i--) {
+ printf("%s", "!");
+ printf("%c", 8); /* BS */
+ esc("A"); /* Cursor Up */
+ }
+ for (i = 2; i <= 23; i++) {
+ vt52cup(i,71);
+ esc("K"); /* Erase to end of line */
+ }
+
+ vt52cup(10,16);
+ printf("%s", "The screen should be cleared, and have a centered");
+ vt52cup(11,16);
+ printf("%s", "rectangle of \"*\"s with \"!\"s on the inside to the");
+ vt52cup(12,16);
+ printf("%s", "left and right. Only this, and");
+ vt52cup(13,16);
+ holdit();
+
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ printf("%s", "This is the normal character set:");
+ for (j = 0; j <= 1; j++) {
+ vt52cup(3 + j, 16);
+ for (i = 0; i <= 47; i++)
+ printf("%c", 32 + i + 48 * j);
+ }
+ vt52cup(6,1);
+ printf("%s", "This is the special graphics character set:");
+ esc("F"); /* Select Special Graphics character set */
+ for (j = 0; j <= 1; j++) {
+ vt52cup(8 + j, 16);
+ for (i = 0; i <= 47; i++)
+ printf("%c", 32 + i + 48 * j);
+ }
+ esc("G"); /* Select ASCII character set */
+ vt52cup(12,1);
+ holdit();
+
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ println("Test of terminal response to IDENTIFY command");
+ esc("Z"); /* Identify */
+ response = instr();
+ println("");
+ printf("Response was");
+ esc("<"); /* Enter ANSI mode (VT100 mode) */
+ chrprint(response);
+ for(i = 0; resptable[i].rcode[0] != '\0'; i++)
+ if (!strcmp(response, resptable[i].rcode))
+ break;
+ printf("%s", resptable[i].rmsg);
+ println("");
+ println("");
+ holdit();
+}
+
+tst_insdel() {
+
+ /* Test of:
+ SM/RM(4) (= IRM (Insertion/replacement mode))
+ ICH (Insert Character)
+ DCH (Delete character)
+ IL (Insert line)
+ DL (Delete line)
+ */
+
+ int i, row, col, sw, dblchr, scr132;
+
+ for(scr132 = 0; scr132 <= 1; scr132++) {
+ if (scr132) { sm("?3"); sw = 132; }
+ else { rm("?3"); sw = 80; }
+ ed(2);
+ cup(1,1);
+ for (row=1; row<=24; row++) {
+ cup(row,1);
+ for (col=1; col<=sw; col++)
+ printf("%c", 'A'-1+row);
+ }
+ cup(4,1);
+ printf("Screen accordion test (Insert & Delete Line). "); holdit();
+ ri(); el(2);
+ decstbm( 2,23);
+ sm("?6");
+ cup(1,1);
+ for (row=1; row<=24; row++) {
+ il(row);
+ dl(row);
+ }
+ rm("?6");
+ decstbm( 0, 0);
+ cup(2,1);
+ printf(
+ "Top line: A's, bottom line: X's, this line, nothing more. ");
+ holdit();
+ cup(2,1); ed(0);
+ cup(1,2);
+ printf("B");
+ cub(1);
+ sm("4");
+ for (col=2; col<=sw-1; col++)
+ printf("*");
+ rm("4");
+ cup(4,1);
+ printf("Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. ");
+ holdit(); ri(); el(2);
+ cup(1,2);
+ dch(sw-2);
+ cup(4,1);
+ printf("Test of 'Delete Character'. The top line should be 'AB'. ");
+ holdit();
+
+ for(dblchr = 1; dblchr <= 2; dblchr++) {
+ ed(2);
+ for (row=1; row<=24; row++) {
+ cup(row,1);
+ if (dblchr == 2) decdwl();
+ for (col=1; col<=sw/dblchr; col++)
+ printf("%c", 'A'-1+row);
+ cup(row,sw/dblchr-row);
+ dch(row);
+ }
+ cup(4,1);
+ println("The right column should be staggered ");
+ printf("by one. ");
+ holdit();
+ }
+ ed(2);
+ cup(1,1);
+ println("If your terminal has the ANSI 'Insert Character' function");
+ println("(the VT102 does not), then you should see a line like this");
+ println(" A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");
+ println("below:");
+ println("");
+ for (i = 'Z'; i >= 'A'; i--) {
+ printf("%c\010",i);
+ ich(2);
+ }
+ cup(10,1);
+ holdit();
+
+ if (sw == 132) rm("?3");
+ }
+}
+
+dch(pn) int pn; { brc(pn, 'P'); } /* Delete character */
+ich(pn) int pn; { brc(pn, '@'); } /* Insert character -- not in VT102 */
+dl(pn) int pn; { brc(pn, 'M'); } /* Delete line */
+il(pn) int pn; { brc(pn, 'L'); } /* Insert line */
+
+/* Test of some known VT100 bugs and misfeatures */
+
+tst_bugs() {
+
+ int i, menuchoice;
+
+ static char *menutable[] = {
+ "Exit to main menu",
+ "Bug A: Smooth scroll to jump scroll",
+ "Bug B: Scrolling region",
+ "Bug C: Wide to narrow screen",
+ "Bug D: Narrow to wide screen",
+ "Bug E: Cursor move from double- to single-wide line",
+ "Bug F: Column mode escape sequence",
+ "Wrap around with cursor addressing",
+ "Erase right half of double width lines",
+ "Funny scroll regions",
+ /* Add more here */
+ ""
+ };
+
+ static char *hmsg[] = {
+ "Test of known bugs in the DEC VT100 series. The numbering of some of",
+ "the bugs (A-F) refers to the article 'VT100 MAGIC' by Sami Tabih in",
+ "the 'Proceedings of the DEC Users Society' at St. Louis, Missouri, May",
+ "1983. To understand some of the tests, you have to look at the source",
+ "code or the article. Of course, a good VT100-compatible terminal",
+ "should not have these bugs (or have some means of disabling them)! If",
+ "a bug appears, you might want to RESET the terminal before continuing",
+ "the test. There is a test of the RESET function in the main menu.",
+ "" };
+
+ do {
+ ed(2); cup(1,1);
+ for (i = 0; *hmsg[i]; i++) println(hmsg[i]);
+ println("");
+ println(" Choose bug test number:");
+ menuchoice = menu(menutable);
+ switch (menuchoice) {
+ case 1: bug_a(); break;
+ case 2: bug_b(); break;
+ case 3: bug_c(); break;
+ case 4: bug_d(); break;
+ case 5: bug_e(); break;
+ case 6: bug_f(); break;
+ case 7: bug_w(); break;
+ case 8: bug_l(); break;
+ case 9: bug_s(); break;
+ }
+ } while (menuchoice);
+}
+
+/* Bug A: Smooth scroll to jump scroll */
+
+bug_a() {
+ int i;
+
+ cup (10, 1);
+ println("This is a test of the VT100 'Scroll while toggle softscroll'");
+ println("bug. The cursor may disappear, or move UP the screen, or");
+ println("multiple copies of some lines may appear.");
+ holdit();
+
+ /* Invoke the bug */
+
+ esc ("[24H"); /* Simplified cursor movement */
+ rm("?4"); for (i = 1; i <= 20; i++) printf("\n");
+ sm("?4"); for (i = 1; i <= 10; i++) printf("\n");
+ rm("?4"); for (i = 1; i <= 5; i++) printf("\n");
+
+ /* That should be enough to show the bug. But we'll try another way: */
+ sm ("?4"); /* Set soft scroll */
+ nel (); /* "NextLine", move down */
+ rm ("?4"); /* Reset soft scroll */
+ nel (); /* "NextLine", move down */
+ for (i = 1; i <= 10; i++) { /* Show the bug */
+ printf ("Softscroll bug test, line %d. ", i);
+ holdit();
+ }
+ println("That should have been enough to show the bug, if present.");
+ holdit();
+}
+
+/* Bug B: Scrolling region */
+
+bug_b() {
+ char c;
+
+ decaln();
+ cup( 1,1); el(0);
+ printf("Line 11 should be double-wide, line 12 should be cleared.");
+ cup( 2,1); el(0);
+ printf("Then, the letters A-P should be written at the beginning");
+ cup( 3,1); el(0);
+ printf("of lines 12-24, and the empty line and A-E are scrolled away.");
+ cup( 4,1); el(0);
+ printf("If the bug is present, some lines are confused, look at K-P.");
+ cup(11,1); decdwl();
+ decstbm(12,24);
+ cup(12,1); el(0); printf("Here we go... "); holdit();
+ cup(12,1); ri(); /* Bug comes here */
+ for (c = 'A'; c <= 'P'; c++) printf("%c\n",c); /* Bug shows here */
+ holdit();
+ decstbm(0,0); /* No scr. region */
+}
+
+/* Bug C: Wide to narrow screen */
+
+bug_c() {
+ sm("?3"); /* 132 column mode */
+ cup(1,81);
+ rm("?3"); /* 80 column mode */
+ cup(12,5);
+ printf("Except for this line, the screen should be blank. ");
+ holdit();
+}
+
+/* Bug D: Narrow to wide screen */
+
+bug_d() {
+ int i;
+ char result;
+ /* Make the bug appear */
+ do {
+ cup(14,1);
+
+ /* The original code in the article says
+ * PRINT ESC$; "[13;1H"; CHR$(10%);
+ * but I guess a cup(14,1); would do.
+ * (To output a pure LF might be tricky).
+ */
+
+ sm("?3"); /* Make the bug visible */
+ cup(1,9); decdwl();
+ println("You should see blinking text at the bottom line.");
+ cup(3,9); decdwl();
+ println("Enter 0 to exit, 1 to try to invoke the bug again.");
+ cup(24,9); decdwl(); sgr("1;5;7");
+ printf("If you can see this then the bug did not appear."); sgr("");
+ cup(4,9); decdwl();
+ result = inchar(); readnl();
+ rm("?3");
+ } while (result == '1');
+ sm("?4"); /* Syrup scroll */
+ cup(23,1);
+ for (i = 1; i <= 5; i++)
+ println("If the bug is present, this should make things much worse!");
+ holdit();
+ rm("?4"); /* Jump scroll */
+}
+
+/* Bug E: Cursor move from double- to single-wide line */
+
+bug_e() {
+ int i;
+ static char *rend[2] = { "\033[m", "\033[7m" };
+ sm("?3");
+ cup(1,1); decdwl();
+ println("This test should put an 'X' at line 3 column 100.");
+ for (i = 1; i <= 12; i++) printf("1234567890%s",rend[i & 1]);
+ cup(1,1); /* The bug appears when we jump from a dobule-wide line */
+ cup(3,100); /* to a single-wide line, column > 66. */
+ printf("X");
+ cup(4, 66); printf("! !");
+ cup(5,1);
+ printf("--------------------------- The 'X' should NOT be above here -");
+ printf("---+------------ but above here -----+");
+ cup(10,1); decdwl(); holdit();
+ rm("?3");
+}
+
+/* Bug F: Column mode escape sequence */
+
+bug_f() {
+ int i, row, col;
+
+ /*
+ * VT100 "toggle origin mode, forget rest" bug. If you try to set
+ * (or clear) parameters and one of them is the "origin mode"
+ * ("?6") parameter, parameters that appear after the "?6"
+ * remain unaffected. This is also true on CIT-101 terminals.
+ */
+ sm ("?5"); /* Set reverse mode */
+ sm ("?3"); /* Set 132 column mode */
+ println("Test VT100 'Toggle origin mode, forget rest' bug, part 1.");
+ println("The screen should be in reverse, 132 column mode.");
+ holdit();
+ ed (2);
+ rm ("?6;5;3"); /* Reset (origin, reverse, 132 col) */
+ println("Test VT100 'Toggle origin mode, forget rest' bug, part 2.\n");
+ println("The screen should be in non-reverse, 80 column mode.");
+ holdit();
+}
+
+ /* Bug W:
+ * The dreaded "wraparound" bug! You CUP to col 80, write a char,
+ * CUP to another line in col 80, write a char. And the brain-damaged
+ * terminal thinks that "Hokay, so he's written a char in col 80, so
+ * I stay in col 80 and wait for next character. Let's see now, here
+ * comes another character, and I'm still in col 80, so I must make
+ * a NewLine first." -- It doesn't clear that "still in col 80" flag
+ * on a CUP. Argh!
+ */
+
+bug_w() {
+ int row, col;
+
+ cup (16,1);
+ println(" This illustrates the \"wrap around bug\" which exists on a");
+ println(" standard VT100. At the top of the screen there should be");
+ println(" a row of +'s, and the rightmost column should be filled");
+ println(" with *'s. But if the bug is present, some of the *'s may");
+ println(" be placed in other places, e.g. in the leftmost column,");
+ println(" and the top line of +'s may be scrolled away.");
+
+ cup(1,1);
+ for (col = 1; col <= 79; col++)
+ printf ("+");
+ for (row = 1; row <= 24; row++) {
+ hvp (row, 80);
+ printf ("*");
+ }
+ cup(24,1);
+ holdit();
+}
+
+ /* Bug L:
+ * Check if the right half of double-width lines comes back
+ * when a line is first set to single-width, filled with stuff,
+ * set to double-width, and finally reset to single-width.
+ *
+ * A VT100 has this misfeature, and many others. Foo!
+ */
+
+bug_l() {
+ cup(15, 1);
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ cup(1, 1);
+ printf("This is a test of what happens to the right half of double-width");
+ println(" lines.");
+ printf("A common misfeature is that the right half does not come back");
+ println(" when a long");
+ printf("single-width line is set to double-width and then reset to");
+ println(" single-width.");
+
+ cup(5, 1);
+ println("Now the line below should contain 80 characters in single width.");
+ holdit();
+ cup(15, 1); decdwl();
+ cup(8, 1);
+ println("Now the line below should contain 40 characters in double width.");
+ holdit();
+ cup(15, 1); decswl();
+ cup(11, 1);
+ println("Now the line below should contain 80 characters in single width.");
+ holdit();
+
+ /* ...and in 132 column mode */
+
+ sm("?3");
+ ed(2);
+ cup(15, 1);
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("ending-here-");
+
+ cup(1, 1);
+ printf("This is the same test in 132 column mode.");
+
+ cup(5, 1);
+ println("Now the line below should contain 132 characters in single width.");
+ holdit();
+ cup(15, 1); decdwl();
+ cup(8, 1);
+ println("Now the line below should contain 66 characters in double width.");
+ holdit();
+ cup(15, 1); decswl();
+ cup(11, 1);
+ println("Now the line below should contain 132 characters in single width.");
+ holdit();
+ rm("?3");
+}
+
+bug_s() {
+ int i;
+ decstbm(20,10); /* 20-10=-10, < 2, so no scroll region. */
+ cup(1,1);
+ for (i=1; i<=20; i++)
+ printf("This is 20 lines of text (line %d), no scroll region.\n", i);
+ holdit();
+ ed(2);
+ decstbm(0,1); /* Should be interpreted as decstbm(1,1) = none */
+ cup(1,1);
+ for (i=1; i<=20; i++)
+ printf("This is 20 lines of text (line %d), no scroll region.\n", i);
+ holdit();
+ decstbm(0,0); /* No scroll region (just in case...) */
+}
+
+tst_rst() {
+
+ /*
+ * Test of
+ * - RIS (Reset to Initial State)
+ * - DECTST (invoke terminal test)
+ */
+
+ cup(10,1);
+ printf ("The terminal will now be RESET. ");
+ holdit();
+ ris();
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ zleep(5000); /* Wait 5.0 seconds */
+ cup(10,1);
+ println("The terminal is now RESET. Next, the built-in confidence test");
+ printf("%s", "will be invoked. ");
+ holdit();
+ ed(2);
+ dectst(1);
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ zleep(5000); /* Wait 5.0 seconds */
+ cup(10,1);
+ println("If the built-in confidence test found any errors, a code");
+ printf("%s", "is visible above. ");
+ holdit();
+}
+
+initterminal(pn) int pn; {
+
+#ifdef UNIX
+ if (pn==0) {
+ fflush(stdout);
+ gtty(0,&sgttyOrg);
+ gtty(0,&sgttyNew);
+ sgttyNew.sg_flags |= CBREAK;
+ }
+ else {
+ fflush(stdout);
+ inflush();
+ sleep(2);
+ sgttyNew.sg_flags = sgttyOrg.sg_flags | CBREAK;
+ }
+ stty(0,&sgttyNew);
+#ifdef SIII
+ close(2);
+ open(_PATH_TTY,O_RDWR|O_NDELAY);
+#endif
+#endif
+#ifdef SARG10
+ /* Set up neccesary TOPS-10 terminal parameters */
+
+ trmop(02041, `VT100`); /* tty type vt100 */
+ trmop(02002, 0); /* tty no tape */
+ trmop(02003, 0); /* tty lc */
+ trmop(02005, 1); /* tty tab */
+ trmop(02010, 1); /* tty no crlf */
+ trmop(02020, 0); /* tty no tape */
+ trmop(02021, 1); /* tty page */
+ trmop(02025, 0); /* tty blanks */
+ trmop(02026, 1); /* tty no alt */
+ trmop(02040, 1); /* tty defer */
+#endif
+#ifdef SARG20
+ ttybin(1); /* set line to binary mode */
+#endif
+ /* Set up my personal prejudices */
+
+ esc("<"); /* Enter ANSI mode (if in VT52 mode) */
+ rm("?1"); /* cursor keys normal */
+ rm("?3"); /* 80 col mode */
+ rm("?4"); /* Jump scroll */
+ rm("?5"); /* Normal screen */
+ rm("?6"); /* Absolute origin mode */
+ sm("?7"); /* Wrap around on */
+ rm("?8"); /* Auto repeat off */
+ decstbm(0,0); /* No scroll region */
+ sgr("0"); /* Normal character attributes */
+
+}
+
+bye () {
+ /* Force my personal prejudices upon the poor luser */
+
+ esc("<"); /* Enter ANSI mode (if in VT52 mode) */
+ rm("?1"); /* cursor keys normal */
+ rm("?3"); /* 80 col mode */
+ rm("?5"); /* Normal screen */
+ rm("?6"); /* Absolute origin mode */
+ sm("?7"); /* Wrap around on */
+ sm("?8"); /* Auto repeat on */
+ decstbm(0,0); /* No scroll region */
+ sgr("0"); /* Normal character attributes */
+
+ /* Say goodbye */
+
+ ed(2);
+ cup(12,30);
+ printf("That's all, folks!\n");
+ printf("\n\n\n");
+ inflush();
+#ifdef SARG20
+ ttybin(0); /* reset line to normal mode */
+#endif
+#ifdef UNIX
+ stty(0,&sgttyOrg);
+#endif
+ exit(0);
+}
+
+#ifdef UNIX
+onbrk() {
+ signal(SIGINT, (void *)onbrk);
+ if (reading)
+ brkrd = 1;
+ else
+ longjmp(intrenv, 1);
+}
+
+onterm() {
+ signal(SIGTERM, (void *)onterm);
+ longjmp(intrenv, 1);
+}
+#endif
+
+holdit() {
+ inflush();
+ printf("Push <RETURN>");
+ readnl();
+}
+
+readnl() {
+#ifdef UNIX
+ char ch;
+ fflush(stdout);
+ brkrd = 0;
+ reading = 1;
+ do { read(STDIN_FILENO,&ch,1); } while(ch != '\n' && !brkrd);
+ if (brkrd)
+ kill(getpid(), SIGTERM);
+ reading = 0;
+#endif
+#ifdef SARG10
+ while (getchar() != '\n')
+ ;
+#endif
+#ifdef SARG20
+ while (getchar() != '\n')
+ ;
+#endif
+}
+
+scanto(str, pos, toc) char *str; int *pos; char toc; {
+ char c;
+ int result = 0;
+
+ while (toc != (c = str[(*pos)++])) {
+ if (isdigit(c)) result = result * 10 + c - '0';
+ else break;
+ }
+ if (c == toc) return(result);
+ else return(0);
+}
+
+char *lookup(t, k) struct table t[]; int k; {
+
+ int i;
+ for (i = 0; t[i].key != -1; i++) {
+ if (t[i].key == k) return(t[i].msg);
+ }
+ return("BAD VALUE");
+}
+
+menu(table) char *table[]; {
+
+ int i, tablesize, choice;
+ char c;
+ char storage[80];
+ char *s = storage;
+ println("");
+ tablesize = 0;
+ for (i = 0; *table[i] != '\0'; i++) {
+ printf(" %d. %s\n", i, table[i]);
+ tablesize++;
+ }
+ tablesize--;
+
+ printf("\n Enter choice number (0 - %d): ", tablesize);
+ for(;;) {
+ inputline(s);
+ choice = 0;
+ while (c = *s++) choice = 10 * choice + c - '0';
+ if (choice >= 0 && choice <= tablesize) {
+ ed(2);
+ return (choice);
+ }
+ printf(" Bad choice, try again: ");
+ }
+}
+
+chrprint (s) char *s; {
+
+ int i;
+
+ printf(" ");
+ sgr("7");
+ printf(" ");
+ for (i = 0; s[i] != '\0'; i++) {
+ if (s[i] <= ' ' || s[i] == '\177')
+ printf("<%d> ", s[i]);
+ else printf("%c ", s[i]);
+ }
+ sgr("");
+}
diff --git a/usr.sbin/pcvt/vttest/vttest.1 b/usr.sbin/pcvt/vttest/vttest.1
new file mode 100644
index 0000000..31f73f5
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/vttest.1
@@ -0,0 +1,21 @@
+.\" $FreeBSD$
+.\"
+.Dd January 16, 2001
+.Dt VTTEST 1
+.Os
+.Sh NAME
+.Nm vttest
+.Nd "test VT100-type terminal"
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility is a program designed to test the functionality of a
+.Tn VT100
+terminal
+(or emulator thereof).
+It tests both display (escape sequence handling) and keyboard.
+.Pp
+The program is menu\-driven and contains full on\-line operating
+instructions.
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..2753bee
--- /dev/null
+++ b/usr.sbin/periodic/periodic.8
@@ -0,0 +1,252 @@
+.\" 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 November 28, 2001
+.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 on Sunday 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.
+.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 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 /usr/X11R6/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 SEE ALSO
+.Xr sh 1 ,
+.Xr crontab 5 ,
+.Xr periodic.conf 5 ,
+.Xr cron 8 ,
+.Xr newsyslog 8
+.Sh DIAGNOSTICS
+Exit status is 0 on success and 1 if the command
+fails 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 HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 .
+.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.
+.Sh AUTHORS
+.An Paul Traina Aq pst@FreeBSD.org
+.An Brian Somers Aq brian@Awfulhak.org
diff --git a/usr.sbin/periodic/periodic.sh b/usr.sbin/periodic/periodic.sh
new file mode 100644
index 0000000..73e8576
--- /dev/null
+++ b/usr.sbin/periodic/periodic.sh
@@ -0,0 +1,106 @@
+#!/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 -s '$host ${arg##*/} run output' $output";;
+ esac
+
+ success=YES info=YES badconfig=NO # Defaults when ${run}_* aren't YES/NO
+ for var in success info badconfig
+ 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
+ [ $processed = 1 ] && plural= || plural=s
+ echo "No output from the $processed file$plural processed"
+ 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..ee4019b
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+SUBDIR= lib add create delete info ${_sign} version
+
+.if !defined(NOCRYPT) && !defined(NO_OPENSSL)
+_sign= sign
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pkg_install/Makefile.inc b/usr.sbin/pkg_install/Makefile.inc
new file mode 100644
index 0000000..e6715ce
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile.inc
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.if exists(${.OBJDIR}/../lib)
+LIBINSTALL= ${.OBJDIR}/../lib/libinstall.a
+.else
+LIBINSTALL= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+.if !defined(NOCRYPT) && !defined(NO_OPENSSL) && \
+ defined(LDADD) && ${LDADD:M-lfetch} != ""
+DISTRIBUTION= crypto
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+# Inherit BINDIR from one level up.
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
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..b72511a
--- /dev/null
+++ b/usr.sbin/pkg_install/add/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= pkg_add
+SRCS= main.c perform.c futil.c extract.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+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..b64cef7
--- /dev/null
+++ b/usr.sbin/pkg_install/add/add.h
@@ -0,0 +1,44 @@
+/* $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 NoInstall;
+extern Boolean NoRecord;
+extern Boolean Force;
+extern char *Mode;
+extern char *Owner;
+extern char *Group;
+extern char *Directory;
+extern char *PkgName;
+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..337fe4d
--- /dev/null
+++ b/usr.sbin/pkg_install/add/extract.c
@@ -0,0 +1,277 @@
+/*
+ * 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 "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, "|tar --unlink -xpf - -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;
+
+ 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 (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)
+
+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;
+ 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;
+ (const char *)Directory = 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 (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
+ (const char *)Directory = 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:
+ 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..66ff738
--- /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("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 && 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 && 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 && 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 && 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..3ce0099
--- /dev/null
+++ b/usr.sbin/pkg_install/add/main.c
@@ -0,0 +1,298 @@
+/*
+ *
+ * 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 <err.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include "lib.h"
+#include "add.h"
+
+static char Options[] = "hvIRfnrp:SMt:C:";
+
+char *Prefix = NULL;
+char *Chroot = NULL;
+Boolean NoInstall = FALSE;
+Boolean NoRecord = FALSE;
+Boolean Remote = FALSE;
+
+char *Mode = NULL;
+char *Owner = NULL;
+char *Group = NULL;
+char *PkgName = NULL;
+char *Directory = NULL;
+char FirstPen[FILENAME_MAX];
+add_mode_t AddMode = NORMAL;
+
+#define MAX_PKGS 200
+char pkgnames[MAX_PKGS][MAXPATHLEN];
+char *pkgs[MAX_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" },
+ { 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" },
+ { 300000, 399000, "/packages-3-stable" },
+ { 400000, 499000, "/packages-4-stable" },
+ { 502100, 599000, "/packages-5-current" },
+ { 0, 9999999, "/packages-current" },
+ { 0, 0, NULL }
+};
+
+static char *getpackagesite(void);
+int getosreldate(void);
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **start;
+ char *cp, *packagesite = NULL, *remotepkg = NULL, *ptr;
+ static char temppackageroot[MAXPATHLEN];
+
+ start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1) {
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'I':
+ NoInstall = TRUE;
+ break;
+
+ case 'R':
+ NoRecord = TRUE;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = 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 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > MAX_PKGS) {
+ errx(1, "too many packages (max %d)", MAX_PKGS);
+ }
+
+ if (AddMode != SLAVE) {
+ for (ch = 0; ch < MAX_PKGS; pkgs[ch++] = NULL) ;
+
+ /* Get all the remaining package names, if any */
+ for (ch = 0; *argv; ch++, argv++) {
+ 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]))
+ /* XXX: need to handle .tgz also */
+ if (strlcat(remotepkg, ".tbz", sizeof(temppackageroot))
+ >= sizeof(temppackageroot))
+ errx(1, "package name too long");
+ }
+ if (!strcmp(*argv, "-")) /* stdin? */
+ (const char *)pkgs[ch] = "-";
+ else if (isURL(*argv)) { /* preserve URLs */
+ if (strlcpy(pkgnames[ch], *argv, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ }
+ else if ((Remote) && isURL(remotepkg)) {
+ if (strlcpy(pkgnames[ch], remotepkg, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ } else { /* expand all pathnames to fullnames */
+ if (fexists(*argv)) /* refers to a file directly */
+ pkgs[ch] = realpath(*argv, pkgnames[ch]);
+ 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(pkgnames[ch], *argv, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ } else {
+ if (strlcpy(pkgnames[ch], cp, sizeof(pkgnames[ch]))
+ >= sizeof(pkgnames[ch]))
+ errx(1, "package name too long");
+ pkgs[ch] = pkgnames[ch];
+ }
+ }
+ }
+ if (packagesite != NULL)
+ packagesite[0] = '\0';
+ }
+ }
+ /* If no packages, yelp */
+ else 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:/usr/sbin:/bin:/usr/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 [-vInrfRMS] [-t template] [-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..ab409c0
--- /dev/null
+++ b/usr.sbin/pkg_install/add/perform.c
@@ -0,0 +1,597 @@
+/*
+ * 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, i, 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))) {
+ 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 %qd 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("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 %qd 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) ||
+ matchbyorigin(Plist.origin, NULL) != NULL) && !Force) {
+ warnx("package '%s' or its older version already installed",
+ Plist.name);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+
+ /* Now check the packing list for conflicts */
+ for (p = Plist.head; p != NULL; p = p->next) {
+ if (p->type == PLIST_CONFLICTS) {
+ 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])) {
+ 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) &&
+ !(deporigin != NULL && matchbyorigin(deporigin, NULL) != NULL)) {
+ char path[FILENAME_MAX], *cp = NULL;
+
+ if (!Fake) {
+ 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("pkg_add %s'%s'", Verbose ? "-v " : "", 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)) != 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; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) {
+ 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 (code != 0)
+ goto bomb;
+
+ /* Look for the requirements file */
+ if (fexists(REQUIRE_FNAME)) {
+ vsystem("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("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("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];
+ 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("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, **depnames;
+ int i;
+
+ 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");
+ }
+
+ depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) :
+ NULL;
+ if (depnames == NULL) {
+ depnames = alloca(sizeof(*depnames) * 2);
+ depnames[0] = p->name;
+ depnames[1] = NULL;
+ }
+ for (i = 0; depnames[i] != NULL; i++) {
+ sprintf(contents, "%s/%s/%s", LOG_DIR, depnames[i],
+ REQUIRED_BY_FNAME);
+ if (strcmp(p->name, depnames[i]) != 0)
+ warnx("warning: package '%s' requires '%s', but '%s' "
+ "is installed", Plist.name, p->name, depnames[i]);
+ 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
+ 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..08bd70a
--- /dev/null
+++ b/usr.sbin/pkg_install/add/pkg_add.1
@@ -0,0 +1,539 @@
+.\"
+.\" 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 March 1, 2004
+.Dt PKG_ADD 1
+.Os
+.Sh NAME
+.Nm pkg_add
+.Nd a utility for installing software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl vInfrRMS
+.Op Fl t Ar template
+.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
+Turn on verbose output.
+.It Fl I
+If an installation scripts (pre-install or post-install) exist for a given
+package, do not execute them.
+.It Fl n
+Don't actually install a package, just report the steps that
+would be taken if it was.
+.It Fl R
+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
+Use the remote fetching feature.
+This will determine the appropriate
+objformat and release and then fetch and install the package.
+.It Fl f
+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 p 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).
+.It Fl t 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
+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
+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 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 "packing list" into a special staging
+directory in /tmp (or $PKG_TMPDIR if set), 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
+.Cm @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
+.Cm @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
+.Cm @option
+directives which control how the package is added to the system.
+At the time of this writing, the only currently implemented option is
+.Cm @option extract-in-place
+which will cause the package to be extracted directly into its
+prefix directory without moving through a staging area in
+.Pa /tmp .
+.It
+If
+.Cm @option 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 the package contains a
+.Ar require
+file (see
+.Xr pkg_create 1 ) ,
+then execute it with the following arguments:
+.Bd -ragged -offset indent -compact
+.Ar pkg-name
+.Ar INSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and the
+.Ar INSTALL
+keyword denotes this as an installation requirements check (useful if
+you want to have one script serving multiple functions).
+.It
+If a
+.Ar pre-install
+script exists for the package, it is then executed with the following
+arguments:
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar pkg-name
+.Ar PRE-INSTALL
+.Ed
+.Pp
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar PRE-INSTALL
+is a keyword denoting this as the preinstallation phase.
+.Pp
+.Sy Note :
+The
+.Ar 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 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 the package contains an
+.Ar mtreefile
+file (see
+.Xr pkg_create 1 ) ,
+then mtree is invoked as:
+.Bd -ragged -offset indent -compact
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix
+.Ed
+where
+.Pa prefix
+is either the prefix specified with the
+.Fl p
+flag or, if no
+.Fl p
+flag was specified, the name of the first directory named by a
+.Cm @cwd
+directive within this package.
+.It
+If a
+.Ar post-install
+script exists for the package, it is then executed as
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar pkg-name
+.Ar POST-INSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar POST-INSTALL
+is a keyword denoting this as the post-installation phase.
+.Pp
+.Sy Note :
+The
+.Ar 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
+.Ar POST-INSTALL
+and
+.Ar PRE-INSTALL
+is that this allows you to write a single
+.Ar install
+script that does both
+.Dq before and after
+actions.
+But, separating the
+functionality is more advantageous and easier from a maintenance viewpoint.
+.It
+After installation is complete, a copy of the packing list,
+.Ar deinstall
+script, description, and display files are copied into
+.Pa /var/db/pkg/<pkg-name>
+for subsequent possible use by
+.Xr pkg_delete 1 .
+Any package dependencies are recorded in the other packages'
+.Pa /var/db/pkg/<other-pkg>/+REQUIRED_BY
+file
+(if the environment variable 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
+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 change it with the
+.Fl p
+flag to
+.Cm pkg_add .
+.Sh ENVIRONMENT
+The value of the
+.Ev PKG_PATH
+is used if a given package can't 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).
+.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..5919769
--- /dev/null
+++ b/usr.sbin/pkg_install/create/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= pkg_create
+SRCS= main.c perform.c pl.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+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..0e806fb
--- /dev/null
+++ b/usr.sbin/pkg_install/create/create.h
@@ -0,0 +1,54 @@
+/* $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 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 *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;
+
+enum zipper {NONE, GZIP, BZIP, BZIP2 };
+extern enum zipper Zipper;
+
+void check_list(const char *, Package *);
+int pkg_perform(char **);
+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..82da981
--- /dev/null
+++ b/usr.sbin/pkg_install/create/main.c
@@ -0,0 +1,210 @@
+/*
+ * 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 <err.h>
+#include "lib.h"
+#include "create.h"
+
+static char Options[] = "YNOhjvyzf:p:P:C:c:d:i:I:k:K:r:t:X:D:m:s:o:b:";
+
+char *Prefix = NULL;
+char *Comment = NULL;
+char *Desc = NULL;
+char *SrcDir = 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;
+enum zipper Zipper = GZIP;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start, *tmp;
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ 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 '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 '?':
+ 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",
+"usage: pkg_create [-YNOhvy] [-P pkgs] [-C conflicts] [-p prefix] [-f contents] ",
+" [-i iscript] [-I piscript] [-k dscript] [-K pdscript] ",
+" [-r rscript] [-t template] [-X excludefile] ",
+" [-D displayfile] [-m mtreefile] [-o origin] ",
+" -c comment -d description -f packlist pkg-filename",
+" pkg_create [-YNhvy] -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..db6847f
--- /dev/null
+++ b/usr.sbin/pkg_install/create/perform.c
@@ -0,0 +1,476 @@
+/*
+ * 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/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(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)
+ return (create_from_installed(pkg, suf));
+
+ 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);
+ write_file(DESC_FNAME, Desc);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DESC_FNAME);
+
+ if (Install) {
+ copy_file(home, Install, INSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, 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);
+ }
+ if (DeInstall) {
+ copy_file(home, DeInstall, DEINSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, 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);
+ }
+ if (Require) {
+ copy_file(home, Require, REQUIRE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, REQUIRE_FNAME);
+ }
+ if (Display) {
+ copy_file(home, Display, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, 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_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)
+{
+ 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;
+
+ 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);
+
+ 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->type == PLIST_SRC)
+ fprintf(totar, "-C\n%s\n", p->name);
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ }
+
+ 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(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, InstalledPkg);
+ if (!fexists(log_dir)) {
+ warnx("can't find package '%s' installed!", InstalledPkg);
+ 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);
+
+ (const char *)Install = isfile(INSTALL_FNAME) ? INSTALL_FNAME : NULL;
+ (const char *)PostInstall = isfile(POST_INSTALL_FNAME) ? POST_INSTALL_FNAME : NULL;
+ (const char *)DeInstall = isfile(DEINSTALL_FNAME) ? DEINSTALL_FNAME : NULL;
+ (const char *)PostDeInstall = isfile(POST_DEINSTALL_FNAME) ? POST_DEINSTALL_FNAME : NULL;
+ (const char *)Require = isfile(REQUIRE_FNAME) ? REQUIRE_FNAME : NULL;
+ (const char *)Display = isfile(DISPLAY_FNAME) ? DISPLAY_FNAME : NULL;
+ (const char *)Mtree = isfile(MTREE_FNAME) ? MTREE_FNAME : NULL;
+
+ make_dist(homedir, pkg, suf, &plist);
+
+ free_plist(&plist);
+ 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..a71abd8
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pkg_create.1
@@ -0,0 +1,558 @@
+.\"
+.\" 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 April 21, 1995
+.Dt PKG_CREATE 1
+.Os
+.Sh NAME
+.Nm pkg_create
+.Nd a utility for creating software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl YNOhjvyz
+.Op Fl C Ar conflicts
+.Op Fl P Ar pkgs
+.Op Fl p Ar prefix
+.Op Fl f Ar contents
+.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 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 YNhvy
+.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
+really 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
+Assume a default answer of `Yes' for any questions asked.
+.It Fl N
+Assume a default answer of `No' for any questions asked.
+.It Fl O
+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
+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 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 t 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 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 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.
+.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 Ar directory
+Set the internal directory pointer to point to
+.Ar directory .
+All subsequent filenames will be assumed relative to this directory.
+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've
+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's later handed off to it, that's
+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 @ignore
+Used internally to tell extraction to ignore the next file (don't
+copy it anywhere), as it's 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
+.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..55c721c
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pl.c
@@ -0,0 +1,257 @@
+/*
+ * 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>
+
+/* 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 *cp, name[FILENAME_MAX], buf[33];
+ PackingList p;
+
+ for (p = pkg->head; p != NULL; p = p->next)
+ switch (p->type) {
+ case PLIST_CWD:
+ where = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ case PLIST_SRC:
+ there = p->name;
+ break;
+
+ case PLIST_FILE:
+ cp = NULL;
+ sprintf(name, "%s/%s", there ? there : where, p->name);
+ if (issymlink(name)) {
+ int len;
+ char lnk[FILENAME_MAX];
+
+ if ((len = readlink(name, lnk, FILENAME_MAX)) > 0)
+ cp = MD5Data((unsigned char *)lnk, len, buf);
+ } else if (isfile(name)) {
+ /* Don't record MD5 checksum for device nodes and such */
+ cp = MD5File(name, 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;
+ p = tmp;
+ }
+ 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("mkdir -p %.*s", cp - to,
+ to);
+ return link(from, to);
+ }
+ return -1;
+}
+
+#define STARTSTRING "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, "|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;
+ 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)
+ where = 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;
+ sprintf(fn, "%s/%s", mythere ? mythere : 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..5c19dba
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= pkg_delete
+SRCS= main.c perform.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 4
+
+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..a973642
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/delete.h
@@ -0,0 +1,36 @@
+/* $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 Force;
+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..dcac1d6
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/main.c
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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 <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static char Options[] = "adDfGhinp:rvx";
+
+char *Prefix = NULL;
+Boolean CleanDirs = FALSE;
+Boolean Interactive = FALSE;
+Boolean NoDeInstall = FALSE;
+Boolean Recursive = FALSE;
+match_t MatchType = MATCH_GLOB;
+
+static void usage __P((void));
+
+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(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ 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 'i':
+ Interactive = TRUE;
+ break;
+
+ case 'r':
+ Recursive = TRUE;
+ break;
+
+ case 'h':
+ case '?':
+ 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 (isalpha(*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 [-dDfGinrvx] [-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..dce1260
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/perform.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
+ * 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_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, **depnames, home[FILENAME_MAX];
+ PackingList p;
+ int i, len;
+ /* support for separate pre/post install scripts */
+ int new_m = 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);
+
+ if (!isinstalledpkg(pkg)) {
+ warnx("no such package '%s' installed", pkg);
+ return 1;
+ }
+
+ if (!getcwd(home, FILENAME_MAX)) {
+ cleanup(0);
+ errx(2, "%s: unable to get current working directory!", __func__);
+ }
+
+ sprintf(LogDir, "%s/%s", LOG_DIR, pkg);
+
+ 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("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("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;
+ }
+ }
+ }
+
+ 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("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;
+ }
+ }
+
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ deporigin = (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) {
+ depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) :
+ NULL;
+ if (depnames == NULL) {
+ depnames = alloca(sizeof(*depnames) * 2);
+ depnames[0] = p->name;
+ depnames[1] = NULL;
+ }
+ for (i = 0; depnames[i] != NULL; i++)
+ undepend(depnames[i], pkg);
+ }
+ }
+ 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..fbcf752
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/pkg_delete.1
@@ -0,0 +1,276 @@
+.\"
+.\" 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 November 25, 1994
+.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 dDfGinrvx
+.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
+Unconditionally delete all currently installed packages.
+.It Fl i
+Request confirmation before attempting to delete each package,
+regardless whether or not the standard input device is a
+terminal.
+.It Fl v
+Turn on verbose output.
+.It Fl D
+If a deinstallation script exists for a given package, do not execute it.
+.It Fl n
+Don't actually deinstall a package, just report the steps that
+would be taken if it were.
+.It Fl p 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
+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 removal of the package, even if a dependency is recorded or the
+deinstall or require script fails.
+.It Fl G
+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
+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 r
+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
+.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..bc90013
--- /dev/null
+++ b/usr.sbin/pkg_install/info/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= pkg_info
+SRCS= main.c perform.c show.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+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..21563ca
--- /dev/null
+++ b/usr.sbin/pkg_install/info/info.h
@@ -0,0 +1,82 @@
+/* $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
+
+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 QUIET;
+extern Boolean UseBlkSz;
+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..c4860ea
--- /dev/null
+++ b/usr.sbin/pkg_install/info/main.c
@@ -0,0 +1,259 @@
+/*
+ *
+ * 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 "lib.h"
+#include "info.h"
+#include <err.h>
+
+static char Options[] = "abcdDe:fgGhiIkl:LmoO:pPqQrRst:vVW:x";
+
+int Flags = 0;
+match_t MatchType = MATCH_GLOB;
+Boolean Quiet = FALSE;
+Boolean QUIET = FALSE;
+Boolean UseBlkSz = FALSE;
+char *InfoPrefix = (char *)(uintptr_t)"";
+char PlayPen[FILENAME_MAX];
+char *CheckPkg = NULL;
+char *LookUpOrigin = NULL;
+struct which_head *whead;
+
+static void usage __P((void));
+
+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(argc, argv, Options)) != -1) {
+ switch(ch) {
+ case 'a':
+ MatchType = MATCH_ALL;
+ break;
+
+ case 'b':
+ UseBlkSz = TRUE;
+ break;
+
+ case 'v':
+ Verbose = TRUE;
+ /* Reasonable definition of 'everything' */
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL |
+ SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE;
+ 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 'k':
+ Flags |= SHOW_DEINSTALL;
+ break;
+
+ case 'r':
+ Flags |= SHOW_REQUIRE;
+ 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 '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':
+ case '?':
+ 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 && !isfile(*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 (isalpha(*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 [-bcdDfgGiIkLmopPqQrRsvVx] [-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..f45f7c7
--- /dev/null
+++ b/usr.sbin/pkg_install/info/perform.c
@@ -0,0 +1,453 @@
+/*
+ * 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 *);
+
+int
+pkg_perform(char **pkgs)
+{
+ char **matched;
+ int err_cnt = 0;
+ int i, errcode;
+
+ signal(SIGINT, cleanup);
+
+ /* Overriding action? */
+ if (CheckPkg) {
+ return isinstalledpkg(CheckPkg) == TRUE ? 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:
+ 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], extrlist[FILENAME_MAX];
+ Package plist;
+ FILE *fp;
+ struct stat sb;
+ char *cp = NULL;
+ int code = 0;
+
+ if (isURL(pkg)) {
+ if ((cp = fileGetURL(NULL, pkg)) != NULL) {
+ strcpy(fname, cp);
+ isTMP = TRUE;
+ }
+ }
+ 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) {
+ /*
+ * 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);
+ snprintf(extrlist, sizeof(extrlist), "--fast-read %s %s %s",
+ CONTENTS_FNAME, COMMENT_FNAME, DESC_FNAME);
+ if (Flags & SHOW_DISPLAY)
+ snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist,
+ DISPLAY_FNAME);
+ if (Flags & SHOW_INSTALL)
+ snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist,
+ INSTALL_FNAME, POST_INSTALL_FNAME);
+ if (Flags & SHOW_DEINSTALL)
+ snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist,
+ DEINSTALL_FNAME, POST_DEINSTALL_FNAME);
+ if (Flags & SHOW_MTREE)
+ snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist,
+ MTREE_FNAME);
+ if (unpack(fname, extrlist)) {
+ warnx("error during unpacking, no info for '%s' available", pkg);
+ code = 1;
+ goto bail;
+ }
+ }
+ /* It's not an ininstalled package, try and find it among the installed */
+ else {
+ if (!isinstalledpkg(pkg)) {
+ 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_REQUIRE)
+ 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_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;
+}
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..9500486
--- /dev/null
+++ b/usr.sbin/pkg_install/info/pkg_info.1
@@ -0,0 +1,241 @@
+.\"
+.\" 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 February 8, 2001
+.Dt PKG_INFO 1
+.Os
+.Sh NAME
+.Nm pkg_info
+.Nd a utility for displaying information on software packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl bcdDfgGiIkLmopPqQrRsvVx
+.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.
+.It Fl a
+Show all currently installed packages.
+.It Fl b
+Use the BLOCKSIZE environment variable for output even when the
+.Fl q
+or
+.Fl Q
+flag is present.
+.It Fl v
+Turn on verbose output.
+.It Fl p
+Show the installation prefix for each package.
+.It Fl q
+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 don't 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 k
+Show the de-install script (if any) for each package.
+.It Fl r
+Show the requirements script (if any) for each package.
+.It Fl R
+Show the list of installed packages which require each package.
+.It Fl m
+Show the mtree 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
+intended to give an idea as to where the underlying port, from which
+package was generated, is located in the
+.Fx
+.Em "Ports Collection" .
+.It Fl G
+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
+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
+.Ev PATH
+is searched using
+.Xr which 1 .
+.It Fl O
+For the specified
+.Ar origin
+argument list all packages having this origin.
+.It Fl x
+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 e Ar pkg-name
+If the package identified by
+.Ar pkg-name
+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 l Ar str
+Prefix each information category header (see
+.Fl q )
+shown with
+.Ar str .
+This is primarily of use to front-end programs who want to request a
+lot of different information fields at once for a package, but don't
+necessary want the output intermingled in such a way that they can't
+organize it. This lets you add a special token to the start of
+each field.
+.It Fl t 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 /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.
+.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
+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 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.
+.El
+.Sh FILES
+.Bl -tag -width /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
+.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..10f846a
--- /dev/null
+++ b/usr.sbin/pkg_install/info/show.c
@@ -0,0 +1,356 @@
+/*
+ * 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;
+
+ 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:
+ printf(Quiet ? "@cwd %s\n" : "\tCWD to %s\n", 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_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;
+ 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:
+ 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;
+
+ 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:
+ 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 tmp[FILENAME_MAX];
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+
+ for (p = plist->head; p != NULL; p = p->next)
+ if (p->type == PLIST_CWD)
+ 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\n", 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..2a79f71
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+LIB= install
+INTERNALLIB= YES
+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
+
+CFLAGS+= ${DEBUG}
+
+WARNS?= 2
+
+.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..4ebb2cf
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/deps.c
@@ -0,0 +1,195 @@
+/*
+ * 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>
+
+/*
+ * 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.
+ */
+int
+sortdeps(char **pkgs)
+{
+ char *tmp;
+ int i, j, loop_cnt;
+ int err_cnt = 0;
+
+ if (pkgs[0] == NULL || pkgs[1] == NULL)
+ return (0);
+
+ for (i = 0; pkgs[i + 1]; i++) {
+ /*
+ * Check to see if any other package in pkgs[i+1:] depends
+ * on pkgs[i] and swap those two packages if so.
+ */
+ loop_cnt = 0;
+ for (j = i + 1; pkgs[j]; j++) {
+ if (chkifdepends(pkgs[j], pkgs[i]) == 1) {
+ /*
+ * 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.
+ * Use ugly but simple method, becase it Should Never
+ * Happen[tm] in the real life anyway.
+ */
+ if (loop_cnt > 4096) {
+ warnx("dependency loop detected for package %s", pkgs[j]);
+ err_cnt++;
+ break;
+ }
+ loop_cnt++;
+ tmp = pkgs[i];
+ pkgs[i] = pkgs[j];
+ pkgs[j] = tmp;
+ /*
+ * Another iteration requred to check if new pkgs[i]
+ * itself has any packages that depend on it
+ */
+ j = i + 1;
+ }
+ }
+ }
+ return err_cnt;
+}
+
+/*
+ * Check to see if pkgname1 depends on pkgname2.
+ * Returns 1 if depends, 0 if not, and -1 if error occured.
+ */
+int
+chkifdepends(const char *pkgname1, const char *pkgname2)
+{
+ char *cp1, *cp2;
+ int errcode;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+ cp2 = strchr(pkgname2, ':');
+ if (cp2 != NULL)
+ *cp2 = '\0';
+ cp1 = strchr(pkgname1, ':');
+ if (cp1 != NULL)
+ *cp1 = '\0';
+
+ errcode = 0;
+ /* Check that pkgname2 is actually installed */
+ if (!isinstalledpkg(pkgname2))
+ goto exit;
+
+ errcode = requiredby(pkgname2, &rb_list, FALSE, TRUE);
+ if (errcode < 0)
+ goto exit;
+
+ errcode = 0;
+ STAILQ_FOREACH(rb_entry, rb_list, link) {
+ if (strcmp(rb_entry->pkgname, pkgname1) == 0) { /* match */
+ errcode = 1;
+ break;
+ }
+ }
+
+exit:
+ if (cp1 != NULL)
+ *cp1 = ':';
+ if (cp2 != NULL)
+ *cp2 = ':';
+ return errcode;
+}
+
+/*
+ * 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)) {
+ 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)) {
+ 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..86285fb
--- /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..b29993c
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/file.c
@@ -0,0 +1,426 @@
+/*
+ * 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))
+ 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 %qd 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, "cp -r %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "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, "mv %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "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, "tar cf - -C %s %s | tar xpf -",
+ dir, fname);
+ }
+ else
+ snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | 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)
+{
+ char *comp, suff[80], *cp;
+
+ 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
+ /* XXX: need to handle .tgz also */
+ comp = "-j";
+ if (vsystem("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..291f48b
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/global.c
@@ -0,0 +1,31 @@
+/*
+ * 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 Verbose = FALSE;
+Boolean Fake = FALSE;
+Boolean Force = FALSE;
+int AutoAnswer = FALSE;
diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h
new file mode 100644
index 0000000..afa68af
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/lib.h
@@ -0,0 +1,226 @@
+/* $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 "rm"
+
+/* Usually "rm", but often "echo" during debugging! */
+#define RMDIR_CMD "rmdir"
+
+/* 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"
+
+#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 20030417
+
+#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
+};
+typedef enum _plist_t plist_t;
+
+enum _match_t {
+ MATCH_ALL, MATCH_EXACT, MATCH_GLOB, 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;
+ char *name;
+ 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 *);
+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 *);
+int isinstalledpkg(const char *name);
+
+/* 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);
+const char *version_of(const char *, int *, int *);
+int version_cmp(const char *, const char *);
+
+/* Externs */
+extern Boolean Verbose;
+extern Boolean Fake;
+extern Boolean Force;
+extern int AutoAnswer;
+
+#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..27db978
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/match.c
@@ -0,0 +1,344 @@
+/*
+ * 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 *);
+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_REGEX, MATCH_GLOB;
+ * 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;
+
+ 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++) {
+ switch (MatchType) {
+ case MATCH_REGEX:
+ errcode = rex_match(patterns[i], f->fts_name);
+ if (errcode == 1) {
+ matched = f->fts_name;
+ errcode = 0;
+ }
+ break;
+ case MATCH_GLOB:
+ if (fnmatch(patterns[i], f->fts_name, 0) == 0) {
+ matched = f->fts_name;
+ lmatched[i] = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ 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;
+}
+
+/*
+ * Synopsis is similar to matchinstalled(), but use origin
+ * as a key for matching packages.
+ */
+char **
+matchbyorigin(const char *origin, int *retval)
+{
+ char **installed;
+ int i;
+ static struct store *store = NULL;
+
+ store = storecreate(store);
+ if (store == NULL) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+
+ if (retval != NULL)
+ *retval = 0;
+
+ installed = matchinstalled(MATCH_ALL, NULL, retval);
+ if (installed == NULL)
+ return NULL;
+
+ for (i = 0; installed[i] != NULL; i++) {
+ FILE *fp;
+ char *cp, tmp[PATH_MAX];
+ int cmd;
+
+ 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) {
+ warn("%s", tmp);
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+
+ 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) {
+ if (strcmp(origin, cp) == 0)
+ storeappend(store, installed[i]);
+ break;
+ }
+ }
+ if (cmd != PLIST_ORIGIN)
+ warnx("package %s has no origin recorded", installed[i]);
+ fclose(fp);
+ }
+
+ if (store->used == 0)
+ return NULL;
+ else
+ return store->store;
+}
+
+/*
+ * Return TRUE if the specified package is installed,
+ * or FALSE otherwise.
+ */
+int
+isinstalledpkg(const char *name)
+{
+ char buf[FILENAME_MAX];
+ char buf2[FILENAME_MAX];
+
+ snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name);
+ if (!isdir(buf) || access(buf, R_OK) == FAIL)
+ return FALSE;
+
+ snprintf(buf2, sizeof(buf2), "%s/%s", buf, CONTENTS_FNAME);
+ if (!isfile(buf2) || access(buf2, R_OK) == FAIL)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * 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)
+{
+ char errbuf[128];
+ int errcode;
+ int retval;
+ regex_t rex;
+
+ retval = 0;
+
+ errcode = regcomp(&rex, pattern, 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;
+}
+
+/*
+ * 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..eedda00
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/pen.c
@@ -0,0 +1,176 @@
+/*
+ * 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 <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;
+
+ 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);
+ 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 %ld bytes\n"
+"free", __func__, (long)sz);
+ 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)
+{
+ 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)
+ fprintf(stderr, "Requested space: %d bytes, free space: %qd bytes in %s\n", (int)sz, (long long)min_free(pen), 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("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..60bc111
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/plist.c
@@ -0,0 +1,579 @@
+/*
+ * 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)
+ (const char *)*arg = 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, "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);
+ 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_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;
+
+ 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:
+ Where = 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 really 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 really 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..5ee33f3
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/url.c
@@ -0,0 +1,144 @@
+/*
+ * 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 <sys/wait.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)
+{
+ char *cp, *rp;
+ char fname[FILENAME_MAX];
+ char pen[FILENAME_MAX];
+ char buf[8192];
+ FILE *ftp;
+ pid_t tpid;
+ int pfd[2], pstat, r, w;
+ char *hint;
+ int fd;
+
+ 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);
+ /* XXX: need to handle .tgz also */
+ 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);
+ /* XXX: need to handle .tgz also */
+ strcat(fname, ".tbz");
+ }
+ }
+ else
+ strcpy(fname, spec);
+
+ 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);
+ /* XXX: need to handle .tgz also */
+ execl("/usr/bin/tar", "tar", Verbose ? "-xjvf" : "-xjf", "-",
+ (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 (ferror(ftp))
+ warn("warning: error reading from server");
+ fclose(ftp);
+ 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..cdc3039
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/version.c
@@ -0,0 +1,171 @@
+/*
+ * 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;
+}
+
+/*
+ * version_of(pkgname, epoch, revision) returns a pointer to the version
+ * portion of a package name and the two special components.
+ *
+ * Jeremy D. Lea.
+ */
+const char *
+version_of(const char *pkgname, int *epoch, int *revision)
+{
+ char *ch;
+
+ if (pkgname == NULL)
+ errx(2, "%s: Passed NULL pkgname.", __func__);
+ if (epoch != NULL) {
+ if ((ch = strrchr(pkgname, ',')) == NULL)
+ *epoch = 0;
+ else
+ *epoch = atoi(&ch[1]);
+ }
+ if (revision != NULL) {
+ if ((ch = strrchr(pkgname, '_')) == NULL)
+ *revision = 0;
+ else
+ *revision = atoi(&ch[1]);
+ }
+ /* Cheat if we are just passed a version, not a valid package name */
+ if ((ch = strrchr(pkgname, '-')) == NULL)
+ return pkgname;
+ else
+ return &ch[1];
+}
+
+/*
+ * 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 below allow for significantly more latitude in the version
+ * numbers than is allowed in the guidelines. No point in wasting user's
+ * time enforcing them here. That's what flamewars are for.
+ *
+ * Jeremy D. Lea.
+ */
+int
+version_cmp(const char *pkg1, const char *pkg2)
+{
+ const char *c1, *c2, *v1, *v2;
+ char *t1, *t2;
+ int e1, e2, r1, r2, n1, n2;
+
+ v1 = version_of(pkg1, &e1, &r1);
+ v2 = version_of(pkg2, &e2, &r2);
+ /* Minor optimisation. */
+ if (strcmp(v1, v2) == 0)
+ return 0;
+ /* First compare epoch. */
+ if (e1 != e2)
+ return (e1 < e2 ? -1 : 1);
+ else {
+ /*
+ * We walk down the versions, trying to convert to numbers.
+ * We terminate when we reach an underscore, a comma or the
+ * string terminator, thanks to a nasty trick with strchr().
+ * strtol() conveniently gobbles up the chars it converts.
+ */
+ c1 = strchr("_,", v1[0]);
+ c2 = strchr("_,", v2[0]);
+ while (c1 == NULL && c2 == NULL) {
+ n1 = strtol(v1, &t1, 10);
+ n2 = strtol(v2, &t2, 10);
+ if (n1 != n2)
+ return (n1 < n2 ? -1 : 1);
+ /*
+ * The numbers are equal, check for letters. Assume they're
+ * letters purely because strtol() didn't chomp them.
+ */
+ c1 = strchr("_,.", t1[0]);
+ c2 = strchr("_,.", t2[0]);
+ if (c1 == NULL && c2 == NULL) {
+ /* Both have letters. Compare them. */
+ if (t1[0] != t2[0])
+ return (t1[0] < t2[0] ? -1 : 1);
+ /* Boring. The letters are equal. Carry on. */
+ v1 = &t1[1], v2 = &t2[1];
+ } else if (c1 == NULL) {
+ /*
+ * Letters are strange. After a number, a letter counts
+ * as greater, but after a period it's less.
+ */
+ return (isdigit(v1[0]) ? 1 : -1);
+ } else if (c2 == NULL) {
+ return (isdigit(v2[0]) ? -1 : 1);
+ } else {
+ /* Neither were letters. Advance over the period. */
+ v1 = (t1[0] == '.' ? &t1[1] : t1);
+ v2 = (t2[0] == '.' ? &t2[1] : t2);
+ }
+ c1 = strchr("_,", v1[0]);
+ c2 = strchr("_,", v2[0]);
+ }
+ /* If we got here, check if one version has something left. */
+ if (c1 == NULL)
+ return (isdigit(v1[0]) ? 1 : -1);
+ if (c2 == NULL)
+ return (isdigit(v2[0]) ? -1 : 1);
+ /* We've run out of version. Try the revision... */
+ if (r1 != r2)
+ return (r1 < r2 ? -1 : 1);
+ else
+ return 0;
+ }
+}
diff --git a/usr.sbin/pkg_install/sign/Makefile b/usr.sbin/pkg_install/sign/Makefile
new file mode 100644
index 0000000..a848441
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+# $OpenBSD: Makefile.bsd-wrapper,v 1.2 1999/10/07 16:30:32 espie Exp $
+
+PROG= pkg_sign
+LINKS= ${BINDIR}/pkg_sign ${BINDIR}/pkg_check
+MLINKS= pkg_sign.1 pkg_check.1
+SRCS= main.c check.c common.c gzip.c pgp_check.c pgp_sign.c \
+ sha1.c sign.c stand.c x509.c
+
+DISTRIBUTION= crypto
+DPADD= ${LIBINSTALL} ${LIBCRYPTO}
+LDADD= ${LIBINSTALL} -lcrypto
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/pkg_install/sign/README b/usr.sbin/pkg_install/sign/README
new file mode 100644
index 0000000..8e81fcd
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/README
@@ -0,0 +1,55 @@
+To sign packages in a transparent way:
+gzip files can handle an extra field at the beginning that
+stores anything we wish.
+
+So it's just a question to choose a format for the signature, and to
+embed it there.
+
+We use the extra field to store signatures. Each signature consists
+of a 6 bytes type marker, a 2 bytes length, followed by the signature
+itself. We can potentially stack signatures: resign a signed archive
+by just prepending the new signature to the extra field.
+
+To check the first signature, the checker just needs to extract it, pass it
+off to the checking protocol (e.g. PGP), followed by the unsigned archive
+(e.g., regenerate the gzip header without the first signature, then put
+the gzip data).
+
+* Signed archives just look like normal .tar.gz files, except for programs
+that use the extra field for their own purpose,
+* Possibility to grab the files off the net and extract stuff/verify
+signatures on the fly (just need to wedge the checker as an intermediate
+pipe)
+* Pretty simple, small portable code to be able to check signatures
+everywhere (the signer itself needs getpass and corresponding functionality)
+
+The scheme should be extensible to any compressed format which allows for
+extended headers.
+
+
+Thanks to Angelos D. Keromytis for pointing out I did not need to
+uncompress the archive to sign it, and to other members of the OpenBSD
+project for various reasons.
+
+--
+ Marc Espie, 1999
+ $OpenBSD: README,v 1.2 1999/10/04 21:46:27 espie Exp $
+
+--
+
+X.509 notes:
+
+I added the ability to sign a package with an X.509 key, and to check
+against a stack of X.509 certificates. This allows a "vendor" to
+distribute a system with one or more certificates pre-installed, and
+to add certificates in a signed package by appending them to the
+default certficiate stack.
+
+The X.509 signatures are stored in the gzip header in the same manner
+as other signatures. This is known to compile against OpenSSL
+libraries on OpenBSD 2.7 and FreeBSD 5.0, your mileage may vary.
+
+--
+
+ Wes Peters, Dec 2000
+ $FreeBSD$
diff --git a/usr.sbin/pkg_install/sign/check.c b/usr.sbin/pkg_install/sign/check.c
new file mode 100644
index 0000000..cfc3bfa
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/check.c
@@ -0,0 +1,119 @@
+/* $OpenBSD: check.c,v 1.2 1999/10/04 21:46:27 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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$");
+
+/* Simple code for a stand-alone package checker */
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+struct checker {
+ void *context;
+ void (*add)(void *, const char *, size_t);
+ int (*get)(void *);
+ int status;
+};
+
+#define MAX_CHECKERS 20
+
+int
+check_signature(file, userid, envp, filename)
+ /*@dependent@*/FILE *file;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ struct signature *sign;
+ struct mygzip_header h;
+ int status;
+ char buffer[1024];
+ size_t length;
+ struct checker checker[MAX_CHECKERS];
+ struct signature *sweep;
+ int i, j;
+
+ status = read_header_and_diagnose(file, &h, &sign, filename);
+ if (status != 1)
+ return PKG_UNSIGNED;
+
+ for (sweep = sign, i = 0;
+ sweep != NULL && i < MAX_CHECKERS;
+ sweep=sweep->next, i++) {
+ switch(sweep->type) {
+ case TAG_OLD:
+ fprintf(stderr, "File %s uses old signatures, no longer supported\n",
+ filename);
+ checker[i].context = NULL;
+ break;
+ case TAG_X509:
+ checker[i].context = new_x509_checker(&h, sweep, userid, envp, filename);
+ checker[i].add = x509_add;
+ checker[i].get = x509_sign_ok;
+ break;
+ case TAG_SHA1:
+ checker[i].context = new_sha1_checker(&h, sweep, userid, envp, filename);
+ checker[i].add = sha1_add;
+ checker[i].get = sha1_sign_ok;
+ break;
+ case TAG_PGP:
+ checker[i].context = new_pgp_checker(&h, sweep, userid, envp, filename);
+ checker[i].add = pgp_add;
+ checker[i].get = pgp_sign_ok;
+ break;
+ default:
+ abort();
+ }
+ }
+ while ((length = fread(buffer, 1, sizeof buffer, file)) > 0) {
+ for (j = 0; j < i; j++) {
+ if (checker[j].context) {
+ (*checker[j].add)(checker[j].context, buffer, length);
+ }
+ }
+ }
+// for (j = i-1; j >= 0; j--)
+ for (j = 0; j < i; j++) {
+ if (checker[j].context) {
+ checker[j].status = (*checker[j].get)(checker[j].context);
+ } else {
+ checker[j].status = PKG_SIGERROR;
+ }
+ }
+ free_signature(sign);
+ return checker[0].status;
+}
+
diff --git a/usr.sbin/pkg_install/sign/common.c b/usr.sbin/pkg_install/sign/common.c
new file mode 100644
index 0000000..ec32357
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/common.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: common.c,v 1.3 1999/10/07 16:30:32 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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/wait.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "stand.h"
+#include "gzip.h"
+#include "pgp.h"
+#include "extern.h"
+
+/* Ensure consistent diagnostics */
+int
+read_header_and_diagnose(file, h, sign, filename)
+ FILE *file;
+ struct mygzip_header *h;
+ struct signature **sign;
+ const char *filename;
+{
+ switch(gzip_read_header(file, h, sign)) {
+ case GZIP_SIGNED:
+ if (sign == NULL) {
+ fprintf(stderr, "File %s is already signed\n", filename);
+ return 0;
+ } else
+ return 1;
+ case GZIP_UNSIGNED:
+ if (sign != NULL) {
+ fprintf(stderr, "File %s is not a signed gzip file\n", filename);
+ return 0;
+ } else
+ return 1;
+ case GZIP_NOT_GZIP:
+ fprintf(stderr, "File %s is not a gzip file\n", filename);
+ return 0;
+ case GZIP_NOT_PGPSIGNED:
+ fprintf(stderr, "File %s contains an unknown extension\n", filename);
+ return 0;
+ default:
+ /* this should not happen */
+ abort();
+ }
+}
+
+int
+reap(pid)
+ pid_t pid;
+{
+ int pstat;
+ pid_t result;
+
+ do {
+ result = waitpid(pid, &pstat, 0);
+ } while (result == -1 && errno == EINTR);
+ return result == -1 ? -1 : pstat;
+}
+
diff --git a/usr.sbin/pkg_install/sign/extern.h b/usr.sbin/pkg_install/sign/extern.h
new file mode 100644
index 0000000..e7c0076
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/extern.h
@@ -0,0 +1,100 @@
+/* $FreeBSD$ */
+/* $OpenBSD: extern.h,v 1.3 1999/10/07 16:30:32 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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.
+ */
+
+/*
+ * Convention: all functions that operate on a FILE * also take a filename
+ * for diagnostic purposes. The file can be connected to a pipe, so
+ * - don't rewind
+ * - don't reopen from filename.
+ */
+
+struct mygzip_header;
+struct signature;
+
+/* main.c */
+extern int verbose;
+extern int quiet;
+extern char *userkey;
+
+/* common.c */
+extern int read_header_and_diagnose __P((FILE *file, \
+ /*@out@*/struct mygzip_header *h, /*@null@*/struct signature **sign, \
+ const char *filename));
+extern int reap __P((pid_t pid));
+
+/* sign.c */
+extern int sign __P((/*@observer@*/const char *filename, int type, \
+ /*@null@*/const char *userid, char *envp[]));
+
+/* check.c */
+extern int check_signature __P((/*@dependent@*/FILE *file, \
+ /*@null@*/const char *userid, char *envp[], \
+ /*@observer@*/const char *filename));
+
+#define PKG_BADSIG 0
+#define PKG_GOODSIG 1
+#define PKG_UNSIGNED 2
+#define PKG_SIGNED 4
+#define PKG_SIGERROR 8
+#define PKG_SIGUNKNOWN 16
+
+typedef /*@observer@*/char *pchar;
+
+#define MAXID 512
+/* sha1.c */
+#define SHA1_DB_NAME "/var/db/pkg/SHA1"
+
+extern void *new_sha1_checker __P((struct mygzip_header *h, \
+ struct signature *sign, const char *userid, char *envp[], \
+ const char *filename));
+
+extern void sha1_add __P((void *arg, const char *buffer, \
+ size_t length));
+
+extern int sha1_sign_ok __P((void *arg));
+
+extern int retrieve_sha1_marker __P((const char *filename, \
+ struct signature **sign, const char *userid));
+
+/* x509.c */
+#define X509_DB_NAME "/var/db/pkg/X509"
+
+extern void *new_x509_checker __P((struct mygzip_header *h, \
+ struct signature *sign, const char *userid, char *envp[], \
+ const char *filename));
+
+extern void x509_add __P((void *arg, const char *buffer, \
+ size_t length));
+
+extern int x509_sign_ok __P((void *arg));
+
+extern int retrieve_x509_marker __P((const char *filename, \
+ struct signature **sign, const char *userid));
diff --git a/usr.sbin/pkg_install/sign/gzip.c b/usr.sbin/pkg_install/sign/gzip.c
new file mode 100644
index 0000000..33d9bae
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/gzip.c
@@ -0,0 +1,319 @@
+/* $OpenBSD: gzip.c,v 1.3 1999/10/04 21:46:28 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "stand.h"
+#include "gzip.h"
+#include "pgp.h"
+
+/*
+ * Signatures follow a simple format
+ * (endianess was chosen to conform to gzip header format)
+ */
+
+SIGNTAG known_tags[KNOWN_TAGS] = {
+ {'S', 'I', 'G', 'P', 'G', 'P', 0, 0 },
+ {'C', 'K', 'S', 'H', 'A', '1', 0, 0 },
+ {'C', 'R', 'X', '5', '0', '9', 0, 0 },
+ {'S', 'i', 'g', 'P', 'G', 'P', 0, 0 } /* old format */
+};
+
+void
+sign_fill_tag(sign)
+ struct signature *sign;
+{
+ sign->tag[6] = sign->length % 256;
+ sign->tag[7] = sign->length / 256;
+}
+
+void
+sign_fill_length(sign)
+ struct signature *sign;
+{
+ sign->length = sign->tag[6] + 256 * sign->tag[7];
+}
+
+static size_t
+stack_sign(match, t, f, sign)
+ SIGNTAG match;
+ int t;
+ FILE *f;
+ struct signature **sign;
+{
+ struct signature *new_sign;
+ size_t length;
+
+ new_sign = malloc(sizeof *new_sign);
+ if (new_sign == NULL)
+ return 0;
+ new_sign->type = t;
+ new_sign->next = NULL;
+ memcpy(new_sign->tag, match, sizeof(SIGNTAG));
+ sign_fill_length(new_sign);
+ new_sign->data = malloc(new_sign->length);
+ if (new_sign->data == NULL ||
+ fread(new_sign->data, 1, new_sign->length, f) != new_sign->length) {
+ free_signature(new_sign);
+ return 0;
+ }
+ length = new_sign->length;
+ if (sign != NULL) {
+ if (!*sign)
+ *sign = new_sign;
+ else {
+ while ((*sign)->next != NULL)
+ sign = &((*sign)->next);
+ (*sign)->next = new_sign;
+ }
+ } else
+ free_signature(new_sign);
+ return length;
+}
+
+
+static int
+add_sign(f, sign)
+ FILE *f;
+ struct signature **sign;
+{
+ SIGNTAG match;
+ int i;
+
+ if (fread(match, 1, sizeof(SIGNTAG), f) != sizeof(SIGNTAG))
+ return -1;
+ for (i = 0; i < KNOWN_TAGS; i++) {
+ if (memcmp(match, known_tags[i], TAGCHECK) == 0) {
+ unsigned int sign_length = stack_sign(match, i, f, sign);
+ if (sign_length > 0)
+ return sign_length + sizeof(SIGNTAG);
+ else
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+gzip_magic(f)
+ FILE *f;
+{
+ int c, d;
+
+ c = fgetc(f);
+ d = fgetc(f);
+ if ((unsigned char)c != (unsigned char)GZIP_MAGIC0
+ || (unsigned char)d != (unsigned char)GZIP_MAGIC1)
+ return 0;
+ else
+ return 1;
+}
+
+static int
+fill_gzip_fields(f, h)
+ FILE *f;
+ struct mygzip_header *h;
+{
+ int method, flags;
+
+ method = fgetc(f);
+ flags = fgetc(f);
+
+ if (method == EOF || flags == EOF || fread(h->stamp, 1, 6, f) != 6)
+ return 0;
+ h->method = (char)method;
+ h->flags = (char)flags;
+ if ((h->flags & CONTINUATION) != 0)
+ if (fread(h->part, 1, 2, f) != 2)
+ return 0;
+ return 1;
+}
+
+/* retrieve a gzip header, including signatures */
+int
+gzip_read_header(f, h, sign)
+ FILE *f;
+ struct mygzip_header *h;
+ struct signature **sign;
+{
+ if (sign != NULL)
+ *sign = NULL;
+ if (!gzip_magic(f) || !fill_gzip_fields(f, h))
+ return GZIP_NOT_GZIP;
+
+ if ((h->flags & EXTRA_FIELD) == 0) {
+ h->remaining = 0;
+ return GZIP_UNSIGNED;
+ }
+ else {
+ int c;
+
+ c = fgetc(f);
+ if (c == EOF)
+ return GZIP_NOT_GZIP;
+ h->remaining = (unsigned)c;
+ c = fgetc(f);
+ if (c == EOF)
+ return GZIP_NOT_PGPSIGNED;
+ h->remaining += ((unsigned) c) << 8;
+ while (h->remaining >= sizeof(SIGNTAG)) {
+ int sign_length = add_sign(f, sign);
+ if (sign_length > 0)
+ h->remaining -= sign_length;
+ if (sign_length < 0)
+ return GZIP_NOT_GZIP;
+ if (sign_length == 0)
+ return GZIP_SIGNED;
+ }
+ return GZIP_SIGNED;
+ }
+}
+
+static unsigned
+sign_length(sign)
+ struct signature *sign;
+{
+ unsigned total = 0;
+
+ while (sign != NULL) {
+ total += sizeof(SIGNTAG) + sign->length;
+ sign = sign->next;
+ }
+ return total;
+}
+
+struct mydata {
+ FILE *file;
+ int ok;
+};
+
+static void myadd(arg, buffer, size)
+ void *arg;
+ const char *buffer;
+ size_t size;
+{
+ struct mydata *d = arg;
+
+ if (fwrite(buffer, 1, size, d->file) == size)
+ d->ok = 1;
+ else
+ d->ok = 0;
+}
+
+/* write a gzip header, including signatures */
+int
+gzip_write_header(f, h, sign)
+ FILE *f;
+ const struct mygzip_header *h;
+ struct signature *sign;
+{
+ struct mydata d;
+ d.file = f;
+ if (gzip_copy_header(h, sign, myadd, &d) == 0)
+ return 0;
+ return d.ok;
+}
+
+int
+gzip_copy_header(h, sign, add, data)
+ const struct mygzip_header *h;
+ struct signature *sign;
+ void (*add)(void *, const char *, size_t);
+ void *data;
+{
+ char flags;
+ size_t length;
+ size_t buflength;
+ size_t i;
+ char *buffer;
+
+ length = h->remaining + sign_length(sign);
+ if (length) {
+ buflength = length + 2;
+ flags = h->flags | EXTRA_FIELD;
+ } else {
+ flags = h->flags & ~EXTRA_FIELD;
+ buflength = 0;
+ }
+ buflength += 10;
+ if ((h->flags & CONTINUATION) != 0)
+ buflength += 2;
+
+ buffer = malloc(buflength);
+ if (buffer == NULL)
+ return 0;
+
+ i = 0;
+ buffer[i++] = GZIP_MAGIC0;
+ buffer[i++] = GZIP_MAGIC1;
+ buffer[i++] = h->method;
+ buffer[i++] = flags;
+ memcpy(buffer+i, h->stamp, 6);
+ i += 6;
+ if ((flags & CONTINUATION) != 0) {
+ memcpy(buffer+i, h->part, 2);
+ i += 2;
+ }
+ if (length) {
+ buffer[i++] = (char)(length % 256);
+ buffer[i++] = (char)(length / 256);
+ while (sign != NULL) {
+ memcpy(buffer+i, sign->tag, sizeof(SIGNTAG));
+ i += sizeof(SIGNTAG);
+ memcpy(buffer+i, sign->data, sign->length);
+ i += sign->length;
+ sign = sign->next;
+ }
+ }
+ (*add)(data, buffer, buflength);
+ free(buffer);
+ return 1;
+}
+
+void
+free_signature(sign)
+ struct signature *sign;
+{
+ struct signature *next;
+
+ while (sign != NULL) {
+ next = sign->next;
+ free(sign->data);
+ free(sign);
+ sign = next;
+ }
+}
diff --git a/usr.sbin/pkg_install/sign/gzip.h b/usr.sbin/pkg_install/sign/gzip.h
new file mode 100644
index 0000000..238010f
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/gzip.h
@@ -0,0 +1,95 @@
+/* $FreeBSD$ */
+/* $OpenBSD: gzip.h,v 1.2 1999/10/04 21:46:28 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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 GZIP_MAGIC0 '\037'
+#define GZIP_MAGIC1 '\213'
+/* flags values */
+#define CONTINUATION 0x02
+#define EXTRA_FIELD 0x04
+
+/*
+ * Meaningful fields in a gzip header, see gzip proper for details.
+ * This structure should not be fiddled with outside of gzip_read_header
+ * and gzip_write_header
+ */
+struct mygzip_header {
+ char method;
+ char flags;
+ char stamp[6];
+ char part[2];
+ /* remaining extra, after know signs have been read */
+ unsigned int remaining;
+};
+
+#define TAGSIZE 8
+#define TAGCHECK 6
+
+typedef unsigned char SIGNTAG[8];
+
+/* stack of signatures */
+struct signature {
+ SIGNTAG tag;
+ int type;
+ int length;
+ char *data;
+ struct signature *next;
+};
+
+/* returns from gzip_read_header */
+#define GZIP_UNSIGNED 0 /* gzip file, no signature */
+#define GZIP_SIGNED 1 /* gzip file, signature parsed ok */
+#define GZIP_NOT_GZIP 2 /* not a proper gzip file */
+#define GZIP_NOT_PGPSIGNED 3 /* gzip file, unknown extension */
+extern int gzip_read_header __P((FILE *f, /*@out@*/struct mygzip_header *h, \
+ /*@null@*/struct signature **sign));
+/* gzip_write_header returns 1 for success */
+extern int gzip_write_header __P((FILE *f, const struct mygzip_header *h, \
+ /*@null@*/struct signature *sign));
+/*
+ * Writing header to memory. Returns size needed, or 0 if buffer too small
+ * buffer must be at least 14 characters
+ */
+extern int gzip_copy_header __P((const struct mygzip_header *h, \
+ /*@null@*/struct signature *sign, \
+ void (*add)(void *, const char *, size_t), void *data));
+
+extern void free_signature __P((/*@null@*/struct signature *sign));
+extern void sign_fill_tag __P((struct signature *sign));
+#define KNOWN_TAGS 4
+#define TAG_PGP 0
+#define TAG_SHA1 1
+#define TAG_X509 2
+#define TAG_OLD 3
+#define TAG_ANY -1
+#define pgptag (known_tags[TAG_PGP])
+#define sha1tag (known_tags[TAG_SHA1])
+#define x509tag (known_tags[TAG_X509])
+extern SIGNTAG known_tags[KNOWN_TAGS];
diff --git a/usr.sbin/pkg_install/sign/main.c b/usr.sbin/pkg_install/sign/main.c
new file mode 100644
index 0000000..c5068b7
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/main.c
@@ -0,0 +1,185 @@
+/* $OpenBSD: main.c,v 1.2 1999/10/04 21:46:28 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "stand.h"
+#include "gzip.h"
+#include "pgp.h"
+#include "extern.h"
+
+#ifdef __OpenBSD__
+extern char *__progname;
+#define argv0 __progname
+#else
+static char *argv0;
+#endif
+
+#define NM_SIGN "pkg_sign"
+
+int verbose = 0;
+int quiet = 0;
+char *userkey = NULL;
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-sc] [-t type] [-u userid] [-k keyfile] pkg1 ...\n", argv0);
+ exit(EXIT_FAILURE);
+}
+
+#define SIGN 0
+#define CHECK 1
+
+/* wrapper for the check_signature function (open file if needed) */
+static int
+check(filename, type, userid, envp)
+ /*@observer@*/const char *filename;
+ int type;
+ /*@null@*/const char *userid;
+ char *envp[];
+{
+ int result;
+ FILE *file;
+
+ if (strcmp(filename, "-") == 0)
+ return check_signature(stdin, userid, envp, "stdin");
+ file = fopen(filename, "r");
+ if (file == NULL) {
+ fprintf(stderr, "Can't open %s\n", filename);
+ return 0;
+ }
+ result = check_signature(file, userid, envp, filename);
+ if (fclose(file) == 0) {
+ if (result == PKG_BADSIG || result == PKG_SIGERROR)
+ return 0;
+ else
+ return 1;
+ } else
+ return 0;
+}
+
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[];
+ char *envp[];
+{
+ int success = 1;
+ int ch;
+ char *userid = NULL;
+ int mode;
+ int i;
+ int type = TAG_ANY;
+
+/* #ifndef BSD4_4 */
+ set_program_name(argv[0]);
+/* #endif */
+#ifdef CHECKER_ONLY
+ mode = CHECK;
+#else
+#ifndef __OpenBSD__
+ if ((argv0 = strrchr(argv[0], '/')) != NULL)
+ argv0++;
+ else
+ argv0 = argv[0];
+#endif
+ if (strcmp(argv0, NM_SIGN) == 0)
+ mode = SIGN;
+ else
+ mode = CHECK;
+#endif
+
+ while ((ch = getopt(argc, argv, "t:u:k:qscv")) != -1) {
+ switch(ch) {
+ case 't':
+ if (strcmp(optarg, "pgp") == 0)
+ type = TAG_PGP;
+ else if (strcmp(optarg, "sha1") == 0)
+ type = TAG_SHA1;
+ else if (strcmp(optarg, "x509") == 0)
+ type = TAG_X509;
+ else
+ usage();
+ break;
+ case 'u':
+ userid = strdup(optarg);
+ break;
+
+ case 'k':
+ userkey = optarg;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+#ifndef CHECKER_ONLY
+ case 's':
+ mode = SIGN;
+ break;
+#endif
+ case 'c':
+ mode = CHECK;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0) {
+ if (mode == CHECK)
+ success &= check("-", 0, userid, envp);
+ else
+ usage();
+ }
+
+#ifndef CHECKER_ONLY
+ if (mode == SIGN && type == TAG_ANY)
+ type = TAG_PGP;
+ if (mode == SIGN && type == TAG_PGP)
+ handle_pgp_passphrase();
+#endif
+ for (i = 0; i < argc; i++)
+ success &= (mode == SIGN ? sign : check)(argv[i], type, userid, envp);
+ exit(success == 1 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/usr.sbin/pkg_install/sign/pgp.h b/usr.sbin/pkg_install/sign/pgp.h
new file mode 100644
index 0000000..99476f2
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pgp.h
@@ -0,0 +1,25 @@
+/* $FreeBSD$ */
+/* $OpenBSD: pgp.h,v 1.2 1999/10/04 21:46:28 espie Exp $ */
+/* Estimate size of pgp signature */
+#define MAXPGPSIGNSIZE 1024
+
+#ifndef PGP
+#define PGP "/usr/local/bin/pgp"
+#endif
+
+struct mygzip_header;
+struct signature;
+
+extern void *new_pgp_checker __P((struct mygzip_header *h, \
+ struct signature *sign, const char *userid, char *envp[], \
+ const char *filename));
+
+extern void pgp_add __P((void *arg, const char *buffer, \
+ size_t length));
+
+extern int pgp_sign_ok __P((void *arg));
+
+extern void handle_pgp_passphrase __P((void));
+
+extern int retrieve_pgp_signature __P((const char *filename, \
+struct signature **sign, const char *userid, char *envp[]));
diff --git a/usr.sbin/pkg_install/sign/pgp_check.c b/usr.sbin/pkg_install/sign/pgp_check.c
new file mode 100644
index 0000000..52b0345
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pgp_check.c
@@ -0,0 +1,196 @@
+/* $OpenBSD: pgp_check.c,v 1.2 1999/10/07 16:30:32 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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 <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+/* transform current process into pgp signature checker -u userid <fd */
+static void
+pgpcheck(fd, userid, envp)
+ int fd;
+ const char *userid;
+ char *envp[];
+{
+ int fdnull;
+ pchar argv[6];
+ int argc = 0;
+
+ argv[argc++] = PGP;
+ argv[argc++] = "+batchmode";
+ argv[argc++] = "-f";
+
+ if (userid) {
+ argv[argc++] = "-u";
+ argv[argc++] = (char *)userid;
+ }
+ argv[argc++] = NULL;
+
+ assert(argc <= sizeof argv / sizeof(pchar));
+
+ fdnull = open(_PATH_DEVNULL, O_RDWR);
+ if (fdnull == -1 ||
+ dup2(fd, fileno(stdin)) == -1 ||
+ dup2(fdnull, fileno(stdout)) == -1 ||
+ close(fdnull) == -1 || close(fd) == -1 ||
+ execve(PGP, argv, envp) == -1)
+ perror("launching pgp");
+ exit(errno);
+}
+
+struct pgp_checker {
+ pid_t id;
+ int fdout;
+ int status;
+#ifdef DEBUG_DUMP
+ FILE *out;
+#endif
+};
+
+void *
+new_pgp_checker(h, sign, userid, envp, filename)
+ struct mygzip_header *h;
+ struct signature *sign;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ struct pgp_checker *n;
+ int topgpcheck[2];
+
+ assert(sign->type == TAG_PGP);
+ n = malloc(sizeof *n);
+
+ {
+ struct stat sbuf;
+
+ if (stat(PGP, &sbuf) == -1) {
+ warnx("%s does not exist", PGP);
+ return NULL;
+ }
+ }
+ if (n == NULL) {
+ warnx("Can't allocate pgp_checker");
+ return NULL;
+ }
+
+ if (pipe(topgpcheck) == -1) {
+ warn("Pgp checker pipe");
+ free(n);
+ return NULL;
+ }
+ switch(n->id = fork()) {
+ case -1:
+ warn("Pgp checker process");
+ free(n);
+ return NULL;
+ case 0:
+ if (close(topgpcheck[1]) == -1)
+ exit(errno);
+ pgpcheck(topgpcheck[0], userid, envp);
+ /*@notreached@*/
+ break;
+ default:
+ (void)close(topgpcheck[0]);
+ break;
+ }
+ n->fdout = topgpcheck[1];
+ /* so that subsequent fork() won't duplicate it inadvertently */
+ (void)fcntl(n->fdout, F_SETFD, FD_CLOEXEC);
+#ifdef DEBUG_DUMP
+ n->out = fopen("compare", "w");
+#endif
+ n->status = PKG_GOODSIG;
+
+ pgp_add(n, sign->data, sign->length);
+ if (gzip_copy_header(h, sign->next, pgp_add, n) == 0) {
+ warnx("Unexpected header in %s", filename);
+ n->status = PKG_SIGERROR;
+ }
+ return n;
+}
+
+void
+pgp_add(arg, buffer, length)
+ void *arg;
+ const char *buffer;
+ size_t length;
+{
+ struct pgp_checker *n = arg;
+
+ if (n->status == PKG_GOODSIG) {
+#ifdef DEBUG_DUMP
+ fwrite(buffer, 1, length, n->out);
+#endif
+ while (length > 0) {
+ ssize_t l = write(n->fdout, buffer, length);
+ if (l == -1) {
+ n->status = PKG_SIGERROR;
+ break;
+ }
+ length -= l;
+ buffer += l;
+ }
+ }
+}
+
+int
+pgp_sign_ok(arg)
+ void *arg;
+{
+ struct pgp_checker *n = arg;
+ int status = n->status;
+
+#ifdef DEBUG_DUMP
+ fclose(n->out);
+#endif
+ if (close(n->fdout) != 0)
+ status = PKG_SIGERROR;
+ if (reap(n->id) != 0)
+ status = PKG_BADSIG;
+ free(n);
+ return status;
+}
diff --git a/usr.sbin/pkg_install/sign/pgp_sign.c b/usr.sbin/pkg_install/sign/pgp_sign.c
new file mode 100644
index 0000000..cb7d186
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pgp_sign.c
@@ -0,0 +1,280 @@
+/* $OpenBSD: pgp_sign.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <assert.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+static void
+pgpsign(fdin, fdout, userid, envp)
+ int fdin, fdout;
+ const char *userid;
+ char *envp[];
+{
+ pchar argv[10];
+ int argc = 0;
+
+ argv[argc++] = PGP;
+ argv[argc++] = "+batchmode";
+ argv[argc++] = "+compress=off";
+ argv[argc++] = "-f";
+ argv[argc++] = "-s";
+ argv[argc++] = "-zAthlon";
+
+ if (userid) {
+ argv[argc++] = "-u";
+ argv[argc++] = (char *)userid;
+ }
+ argv[argc++] = NULL;
+ assert(argc <= sizeof argv / sizeof(pchar));
+
+ if (dup2(fdin, fileno(stdin)) == -1 ||
+ dup2(fdout, fileno(stdout)) == -1 ||
+ execve(PGP, argv, envp) == -1)
+ exit(errno);
+}
+
+static struct signature *
+new_pgpsignature(old)
+ struct signature *old;
+{
+ struct signature *n;
+
+ n = malloc(sizeof(*n));
+ if (n != NULL) {
+ n->data = malloc(MAXPGPSIGNSIZE);
+ if (n->data == NULL) {
+ free(n);
+ return NULL;
+ }
+ n->length = 0;
+ n->next = old;
+ n->type = TAG_PGP;
+ memcpy(n->tag, pgptag, sizeof pgptag);
+ }
+ return n;
+}
+
+int
+retrieve_pgp_signature(filename, sign, userid, envp)
+ const char *filename;
+ struct signature **sign;
+ const char *userid;
+ char *envp[];
+{
+ int topgp[2], frompgp[2];
+ pid_t pgpid;
+ struct mygzip_header h;
+ int success;
+
+ FILE *orig, *dest, *signin;
+ struct signature *old;
+
+ orig = fopen(filename, "r");
+ if (orig == NULL)
+ return 0;
+ if (gzip_read_header(orig, &h, &old) == GZIP_NOT_GZIP) {
+ warnx("File %s is not a gzip file\n", filename);
+ fclose(orig);
+ return 0;
+ }
+
+ if (pipe(topgp) == -1) {
+ fclose(orig);
+ return 0;
+ }
+ if (pipe(frompgp) == -1) {
+ fclose(orig);
+ (void)close(topgp[0]);
+ (void)close(topgp[1]);
+ return 0;
+ }
+ switch(pgpid = fork()) {
+ case 0:
+ (void)close(topgp[1]);
+ (void)close(frompgp[0]);
+ pgpsign(topgp[0], frompgp[1], userid, envp);
+ /*NOT REACHED */
+ case -1:
+ (void)close(topgp[0]);
+ (void)close(topgp[1]);
+ (void)close(frompgp[0]);
+ (void)close(frompgp[1]);
+ fclose(orig);
+ return 0;
+ default:
+ (void)close(topgp[0]);
+ (void)close(frompgp[1]);
+ }
+
+ dest = fdopen(topgp[1], "w");
+ if (dest == NULL) {
+ (void)close(topgp[1]);
+ (void)close(frompgp[0]);
+ (void)reap(pgpid);
+ return 0;
+ }
+
+ success = 1;
+ if (gzip_write_header(dest, &h, old) == 0)
+ success = 0;
+ else {
+ int c;
+
+ while ((c = fgetc(orig)) != EOF && fputc(c, dest) != EOF)
+ ;
+ if (ferror(dest))
+ success = 0;
+ }
+ if (fclose(dest) != 0)
+ success = 0;
+
+ if (fclose(orig) != 0)
+ success = 0;
+
+ signin = fdopen(frompgp[0], "r");
+ if (signin == NULL) {
+ (void)close(frompgp[0]);
+ } else {
+ enum { NONE, FIRST, DONE, COPY} magic = NONE;
+ int c;
+#ifdef DEBUG_DUMP
+ FILE *out = fopen("dump", "w");
+#endif
+
+ if ((*sign = new_pgpsignature(old)) == NULL)
+ success = 0;
+ else {
+ while ((c = fgetc(signin)) != EOF && magic != DONE &&
+ (*sign)->length < MAXPGPSIGNSIZE) {
+ switch(magic) {
+ case NONE:
+ (*sign)->data[(*sign)->length++] = c;
+ if ((unsigned char)c == (unsigned char)GZIP_MAGIC0)
+ magic = FIRST;
+ break;
+ case FIRST:
+ (*sign)->data[(*sign)->length++] = c;
+ if ((unsigned char)c == (unsigned char)GZIP_MAGIC1)
+#ifdef DEBUG_DUMP
+ magic = COPY;
+#else
+ magic = DONE;
+#endif
+ else if ((unsigned char)c != (unsigned char)GZIP_MAGIC0)
+ magic = NONE;
+ break;
+ case DONE:
+ case COPY:
+ break;
+ }
+#ifdef DEBUG_DUMP
+ fputc(c, out);
+#endif
+ }
+ if ((*sign)->length == MAXPGPSIGNSIZE)
+ success = 0;
+ (*sign)->length -= 2;
+ sign_fill_tag(*sign);
+ }
+ fclose(signin);
+#ifdef DEBUG_DUMP
+ fclose(out);
+#endif
+ reap(pgpid);
+ }
+ return success;
+}
+
+void
+handle_pgp_passphrase()
+{
+ pid_t pid;
+ int fd[2];
+ char *p;
+
+printf("Short-circuiting %s\n", __func__);
+return;
+
+ /* Retrieve the pgp passphrase */
+ p = getpass("Enter passphrase:");
+
+ /*
+ * Somewhat kludgy code to get the passphrase to pgp, see
+ * pgp documentation for the gore
+ */
+ if (pipe(fd) != 0) {
+ perror("pkg_sign");
+ exit(EXIT_FAILURE);
+ }
+ switch(pid = fork()) {
+ case -1:
+ perror("pkg_sign");
+ exit(EXIT_FAILURE);
+ case 0:
+ {
+ (void)close(fd[0]);
+ /*
+ * The child fills the pipe with copies of the passphrase.
+ * Expect violent death when father exits.
+ */
+ printf("Child process %d stuffing passphrase in pipe:\n", getpid());
+ for(;;) {
+ char c = '\n';
+ (void)write(fd[1], p, strlen(p));
+ (void)write(fd[1], &c, 1);
+ putchar('.'); fflush(stdout);
+ }
+ }
+ default:
+ {
+ char buf[10];
+
+ sleep(1);
+ (void)close(fd[1]);
+ (void)sprintf(buf, "%d", fd[0]);
+ (void)setenv("PGPPASSFD", buf, 1);
+ printf("Parent process PGPPASSFD=%d.\n", fd[0]);
+ }
+ }
+}
+
diff --git a/usr.sbin/pkg_install/sign/pkg_sign.1 b/usr.sbin/pkg_install/sign/pkg_sign.1
new file mode 100644
index 0000000..e8af93d
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/pkg_sign.1
@@ -0,0 +1,209 @@
+.\" $FreeBSD$
+.\" $OpenBSD: pkg_sign.1,v 1.6 2000/04/15 02:15:20 aaron Exp $
+.\"
+.\" Copyright (c) 1999 Marc Espie.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+.\" Project.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+.\" PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.Dd September 24, 1999
+.Dt PKG_SIGN 1
+.Os
+.Sh NAME
+.Nm pkg_sign ,
+.Nm pkg_check
+.Nd handle package signatures
+.Sh SYNOPSIS
+.Nm
+.Op Fl sc
+.Op Fl t Ar type
+.Op Fl u Ar id
+.Op Fl k Ar key
+.Op Ar
+.Nm pkg_check
+.Op Fl sc
+.Op Fl u Ar id
+.Op Fl k Ar cert
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility embeds a cryptographic signature within a gzip file
+.Ar file .
+.Ar type
+can be
+.Cm pgp
+(default),
+.Cm sha1 ,
+or
+.Cm x509 .
+If
+.Ar type
+is
+.Cm pgp ,
+it will always prompt you for a passphrase to unlock your private
+pgp key, even if you don't use a passphrase (which is a bad idea, anyway).
+If
+.Ar type
+is
+.Cm sha1 ,
+you must supply an
+.Ar id ,
+which will be recorded as the name of the package, and printed as the
+SHA1 checksum.
+.Pp
+The
+.Nm pkg_check
+utility checks that cryptographic signature.
+It currently disregards
+.Ar type
+and checks only the topmost signature.
+For
+.Cm sha1 ,
+it checksums the file
+and verifies that the result matches the list of checksums recorded in
+.Pa /var/db/pkg/SHA1 .
+.Pp
+Options
+.Fl s
+and
+.Fl c
+can be used to force package signing or signature checking mode.
+.Pp
+For
+.Cm pgp ,
+the
+.Ar id
+to use to sign the package or verify the signature can be forced with
+.Fl u .
+.Pp
+For
+.Cm x509 ,
+the signing key or verification certificate may be
+specified with the
+.Fl k
+option. If not specified, packages are signed or verified with the
+default keys and certificates documented below.
+.Pp
+If
+.Ar file
+is a single dash
+.Pq Sq Fl
+or absent,
+.Nm
+reads from the standard input.
+.Pp
+Package signing uses a feature of the gzip format, namely that one can
+set a flag
+.Dv EXTRA_FIELD
+in the gzip header and store extra data between the gzip header and the
+compressed file proper.
+The
+.Ox
+signing scheme uses eight bytes markers such
+.Sq Li SIGPGP
++ length or
+.Sq CKSHA1
++ length for its signatures (those markers are conveniently
+eight bytes long).
+.Sh DIAGNOSTICS
+The
+.Nm
+and
+.Nm pkg_check
+utilities return with an exit code >0 if anything went wrong for any
+.Ar file .
+For
+.Nm pkg_check ,
+this usually indicates that the package is not signed, or that the
+signature is forged.
+.Bl -diag
+.It "File %s is already signed"
+There is a signature embedded within the gzip file already.
+The
+.Nm
+utility currently does not handle multiple signatures.
+.It "File %s is not a signed gzip file"
+This is an unsigned package.
+.It "File %s is not a gzip file"
+The program couldn't find a proper gzip header.
+.It "File %s contains an unknown extension"
+The extended area of the gzip file has been used for an unknown purpose.
+.It "File %s uses old signatures, no longer supported"
+The gzip file uses a very early version of package signing that was
+substantially slower.
+.El
+.Sh BUGS
+The
+.Xr pgp 1
+utility is an ill-designed program, which is hard to interface with.
+For instance, the `separate signing scheme' it pretends to offer is
+useless, as it can't be used with pipes, so that
+.Nm pgp_sign
+needs to kludge it by knowing the length of a pgp signature, and invoking
+pgp in `seamless' signature mode, without compression of the main file,
+and just retrieving the signature.
+.Pp
+The checking scheme is little less convoluted, namely we rebuild the file
+that pgp expects on the fly.
+.Pp
+Paths to
+.Nm pgp
+and
+the checksum file are hard-coded to avoid tampering and hinder flexibility.
+.Sh FILES
+.Bl -tag -width "/usr/local/bin/pgp" -compact
+.It Pa file.sign
+Temporary file built by
+.Nm
+from
+.Ar file .
+.It Pa /usr/local/bin/pgp
+Default path to
+.Xr pgp 1 .
+.It Pa /var/db/pkgs/SHA1
+Recorded checksums.
+.It Pa /etc/ssl/pkg.key
+Default package signing key.
+.It Pa /etc/ssl/pkg.crt
+Default package verification certificate(s).
+.El
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr pgp 1 ,
+.Xr pkg_add 1 ,
+.Xr sha1 1
+.Sh AUTHORS
+.An -nosplit
+A
+.Nm
+utility was created by
+.An Marc Espie
+for the
+.Ox
+Project.
+X.509 signatures and
+.Fx
+support added by
+.An Wes Peters Aq wes@softweyr.com .
diff --git a/usr.sbin/pkg_install/sign/sha1.c b/usr.sbin/pkg_install/sign/sha1.c
new file mode 100644
index 0000000..24ca997
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/sha1.c
@@ -0,0 +1,224 @@
+/* $OpenBSD: sha1.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <openssl/sha.h>
+#include "stand.h"
+#include "gzip.h"
+#include "extern.h"
+
+/* private context for sha1 signature checker */
+struct sha1_checker {
+ SHA_CTX context;
+ const char *id;
+ const char *filename;
+};
+
+
+#define SHA1_TEMPLATE "SHA1 (%s) = "
+#define BUFSIZE (MAXID+sizeof(SHA1_TEMPLATE)+2*SHA_DIGEST_LENGTH+1)
+
+/*
+ * Finalize SHA1 checksum for our sha1_context into result
+ * (size at least BUFSIZE). Returns the length of the checksum
+ * marker, e.g., SHA1 (id) = xxxxxxxxx
+ * ^here
+ * Return 0 for errors.
+ */
+size_t
+sha1_build_checksum(result, n)
+ char *result;
+ struct sha1_checker *n;
+{
+ size_t length;
+
+ sprintf(result, "SHA1 (%s) = ", n->id);
+ length = strlen(result);
+ SHA1_Final(result + length, &n->context);
+ strcat(result, "\n");
+ free(n);
+ return length;
+}
+
+void *
+new_sha1_checker(h, sign, userid, envp, filename)
+ struct mygzip_header *h;
+ struct signature *sign;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ struct sha1_checker *n;
+
+ assert(sign->type == TAG_SHA1);
+ /* make sure data conforms to what we can handle */
+ if (sign->length > MAXID || sign->data[sign->length-1] != '\0') {
+ warnx("Corrupted SHA1 header in %s", filename);
+ return 0;
+ }
+
+ n = malloc(sizeof *n);
+ if (n == NULL) {
+ warnx("Can't allocate sha1_checker");
+ return NULL;
+ }
+ SHA1_Init(&n->context);
+ n->id = sign->data;
+ n->filename = filename;
+
+ /* copy header, as this is a checksum, we don't strip our own marker */
+ if (gzip_copy_header(h, sign, sha1_add, n) == 0) {
+ warnx("Unexpected header in %s", filename);
+ free(n);
+ return 0;
+ }
+ return n;
+}
+
+void
+sha1_add(arg, buffer, length)
+ void *arg;
+ const char *buffer;
+ size_t length;
+{
+ struct sha1_checker *n = arg;
+ SHA1_Update(&n->context, buffer, length);
+}
+
+int
+sha1_sign_ok(arg)
+ void *arg;
+{
+ struct sha1_checker *n = arg;
+ char buffer[BUFSIZE];
+ char scan[BUFSIZE];
+ size_t length;
+ FILE *f;
+ int tag_found;
+
+ length = sha1_build_checksum(buffer, n);
+ f= fopen(SHA1_DB_NAME, "r");
+ tag_found = 0;
+
+ if (f == NULL) {
+ warn("Can't access checksum file %s", SHA1_DB_NAME);
+ return PKG_BADSIG;
+ }
+ while (fgets(scan, sizeof(scan), f) != NULL) {
+ if (strcmp(scan, buffer) == 0) {
+ fprintf(stderr, "Checksum ok\n");
+ return PKG_GOODSIG;
+ }
+ if (strncmp(scan, buffer, length) == 0)
+ tag_found = 1;
+ }
+
+ if (tag_found) {
+ warnx("Checksum incorrect for %s (%s)", n->filename, n->id);
+ return PKG_BADSIG;
+ } else {
+ warnx("No checksum found for %s (%s)", n->filename, n->id);
+ return PKG_SIGUNKNOWN;
+ }
+}
+
+int
+retrieve_sha1_marker(filename, sign, userid)
+ const char *filename;
+ struct signature **sign;
+ const char *userid;
+{
+ struct signature *n;
+ struct mygzip_header h;
+ FILE *f;
+ char buffer[1024];
+ char result[BUFSIZE];
+ ssize_t length;
+ struct sha1_checker *checker;
+ struct signature *old;
+
+ *sign = NULL;
+ if (userid == NULL)
+ return 0;
+
+ /*
+ * Create a blank signature and fill it with the userid.
+ */
+ n = malloc(sizeof *n);
+ if (n == NULL)
+ return 0;
+ n->data = (char *)userid;
+ n->length = strlen(n->data)+1;
+ n->type = TAG_SHA1;
+ memcpy(n->tag, sha1tag, sizeof sha1tag);
+ sign_fill_tag(n);
+
+ /*
+ * Read the gzip header and add our "userid" signature to it.
+ */
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ free(n);
+ return 0;
+ }
+ if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) {
+ warnx("File %s is not a gzip file\n", filename);
+ fclose(f);
+ free(n);
+ return 0;
+ }
+ n->next = *sign;
+ *sign = n;
+
+ /*
+ * Calculate the SHA1 of the remaining data and write it to stderr.
+ */
+ checker = new_sha1_checker(&h, *sign, NULL, NULL, filename);
+ while ((length = fread(buffer, 1, sizeof buffer, f)) > 0)
+ sha1_add(checker, buffer, length);
+ if (fclose(f) != 0 || length == -1) {
+ warn("Problem checksumming %s", filename);
+ *sign = n->next;
+ free(n);
+ return 0;
+ }
+
+ (void)sha1_build_checksum(result, checker);
+ fputs(result, stderr);
+ return 1;
+}
+
diff --git a/usr.sbin/pkg_install/sign/sign.c b/usr.sbin/pkg_install/sign/sign.c
new file mode 100644
index 0000000..412b916
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/sign.c
@@ -0,0 +1,146 @@
+/* $OpenBSD: sign.c,v 1.3 1999/10/04 21:46:29 espie Exp $ */
+/*-
+ * Copyright (c) 1999 Marc Espie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided 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 Marc Espie for the OpenBSD
+ * Project.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD 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 OPENBSD
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <assert.h>
+#include "stand.h"
+#include "pgp.h"
+#include "gzip.h"
+#include "extern.h"
+
+#define COPY_TEMPLATE "%s.sign"
+
+static int
+embed_signature_FILE(orig, dest, sign, filename)
+ /*@temp@*/FILE *orig;
+ /*@temp@*/FILE *dest;
+ struct signature *sign;
+ const char *filename;
+{
+ struct mygzip_header h;
+ int c;
+
+ if (gzip_read_header(orig, &h, NULL) == GZIP_NOT_GZIP)
+ return 0;
+
+ if (gzip_write_header(dest, &h, sign) == 0)
+ return 0;
+ while ((c = fgetc(orig)) != EOF && fputc(c, dest) != EOF)
+ ;
+ if (ferror(dest) != 0)
+ return 0;
+ return 1;
+}
+
+static int
+embed_signature(filename, copy, sign)
+ const char *filename;
+ const char *copy;
+ struct signature *sign;
+{
+ FILE *orig, *dest;
+ int success;
+
+ success = 0;
+ orig= fopen(filename, "r");
+ if (orig) {
+ dest = fopen(copy, "w");
+ if (dest) {
+ success = embed_signature_FILE(orig, dest, sign, filename);
+ if (fclose(dest) != 0)
+ success = 0;
+ }
+ if (fclose(orig) != 0)
+ success = 0;
+ }
+ return success;
+}
+
+int
+sign(filename, type, userid, envp)
+ const char *filename;
+ const char *userid;
+ int type;
+ char *envp[];
+{
+ char *copy;
+ int result;
+ struct signature *sign;
+ int success;
+
+ sign = NULL;
+ switch(type) {
+ case TAG_PGP:
+ success = retrieve_pgp_signature(filename, &sign, userid, envp);
+ break;
+ case TAG_SHA1:
+ success = retrieve_sha1_marker(filename, &sign, userid);
+ break;
+ case TAG_X509:
+ success = retrieve_x509_marker(filename, &sign, userid);
+ break;
+ }
+
+ if (!success) {
+ fprintf(stderr, "Problem signing %s\n", filename);
+ free_signature(sign);
+ return 0;
+ }
+ copy = malloc(strlen(filename)+sizeof(COPY_TEMPLATE));
+ if (copy == NULL) {
+ fprintf(stderr, "Can't allocate memory\n");
+ free_signature(sign);
+ return 0;
+ }
+ sprintf(copy, COPY_TEMPLATE, filename);
+ result = embed_signature(filename, copy, sign);
+ if (result == 0) {
+ fprintf(stderr, "Can't embed signature in %s\n", filename);
+ } else if (unlink(filename) != 0) {
+ fprintf(stderr, "Can't unlink original %s\n", filename);
+ result = 0;
+ } else if (rename(copy, filename) != 0) {
+ fprintf(stderr, "Can't rename new file %s\n", copy);
+ result = 0;
+ }
+ free(copy);
+ free_signature(sign);
+ return result;
+}
+
diff --git a/usr.sbin/pkg_install/sign/stand.c b/usr.sbin/pkg_install/sign/stand.c
new file mode 100644
index 0000000..703c58d
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/stand.c
@@ -0,0 +1,57 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "stand.h"
+
+#ifdef BSD4_4
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+
+/* shortened version of warn */
+static const char *program_name;
+
+void
+set_program_name(n)
+ const char *n;
+{
+ if ((program_name = strrchr(n, '/')) != NULL)
+ program_name++;
+ else
+ program_name = n;
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list ap;
+ int interrno;
+
+ va_start(ap, fmt);
+
+ interrno = errno;
+ (void)fprintf(stderr, "%s: ", program_name);
+ if (fmt != NULL) {
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, ": ");
+ }
+ (void)fprintf(stderr, "%s\n", strerror(interrno));
+
+ va_end(ap);
+}
+
+void
+warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", program_name);
+ if (fmt != NULL)
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+#endif
diff --git a/usr.sbin/pkg_install/sign/stand.h b/usr.sbin/pkg_install/sign/stand.h
new file mode 100644
index 0000000..bc8de2f
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/stand.h
@@ -0,0 +1,28 @@
+/* $FreeBSD$ */
+/* $OpenBSD: stand.h,v 1.2 1999/10/04 21:46:30 espie Exp $ */
+
+/* provided to cater for BSD idiosyncrasies */
+
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
+
+#if defined(BSD4_4)
+#include <err.h>
+#else
+extern void set_program_name __P((const char * name));
+extern void warn __P((const char *fmt, ...));
+extern void warnx __P((const char *fmt, ...));
+#endif
+
+#ifndef __GNUC__
+#define __attribute__(x)
+#endif
diff --git a/usr.sbin/pkg_install/sign/x509.c b/usr.sbin/pkg_install/sign/x509.c
new file mode 100644
index 0000000..837a81e
--- /dev/null
+++ b/usr.sbin/pkg_install/sign/x509.c
@@ -0,0 +1,427 @@
+/*-
+ * Copyright (c) 2000 Softweyr LLC, South Jordan, Utah, USA.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Softweyr LLC ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL Softweyr LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING 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/wait.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+
+#include "stand.h"
+#include "gzip.h"
+#include "extern.h"
+
+
+/*
+ * Default names for the signing key and certificate(s) for verification.
+ */
+#define CERTFILE "/etc/ssl/pkg.crt"
+#define KEYFILE "/etc/ssl/pkg.key"
+
+
+/*
+ * Private context for X.509 signature checker
+ */
+struct x509_checker
+{
+ const char * id;
+ const char * filename;
+
+ struct signature * signature;
+
+ STACK_OF(X509) * certs;
+ EVP_MD_CTX rsa_ctx, dsa_ctx;
+ int has_rsa, has_dsa;
+};
+
+
+static void key_from_name(char *, const char *);
+
+/*
+ * Initialize an X.509 "checker" context.
+ */
+void *
+new_x509_checker(h, sign, userid, envp, filename)
+ struct mygzip_header *h;
+ struct signature *sign;
+ const char *userid;
+ char *envp[];
+ /*@observer@*/const char *filename;
+{
+ FILE * fp;
+ struct x509_checker * me;
+ char certfile[PATH_MAX + 1] = CERTFILE;
+ char * cp;
+ X509 * x509;
+
+ assert(sign->type == TAG_X509);
+
+ /*
+ * Make sure data conforms to what we can handle. We do not write a
+ * trailing null onto the signature like some other types, because
+ * the X.509 signature is binary data.
+ */
+ if (sign->length > MAXID) {
+ warnx("Corrupted X.509 header in %s", filename);
+ return 0;
+ }
+
+ me = malloc(sizeof *me);
+ if (me == NULL) {
+ warn("Cannot allocate x509_checker");
+ return 0;
+ }
+ me->id = sign->data;
+ me->filename = filename;
+ me->signature = sign;
+ me->has_rsa = 0;
+ me->has_dsa = 0;
+
+ key_from_name(certfile, userkey);
+
+ /*
+ * Load just the crypto library error strings.
+ */
+ ERR_load_crypto_strings();
+
+ /*
+ * Load the stack of X.509 certs we will compare against.
+ *
+ * KLUDGE: this needs to be fleshed out a bit. We can do better
+ * than hard-coding the location of the cert key file.
+ */
+ me->certs = sk_X509_new_null();
+
+ fp = fopen(certfile, "r");
+ if (fp == NULL) {
+ warnx("Cannot open public key %s", certfile);
+ return 0;
+ }
+
+ if (verbose)
+ printf("Loading certificates from %s:\n", certfile);
+
+ while (x509 = PEM_read_X509(fp, NULL, NULL, 0)) {
+ sk_X509_push(me->certs, x509);
+
+ switch (EVP_PKEY_type(X509_get_pubkey(x509)->type))
+ {
+ case EVP_PKEY_RSA:
+ me->has_rsa = 1;
+ break;
+
+ case EVP_PKEY_DSA:
+ me->has_dsa = 1;
+ break;
+
+ default:
+ warnx("Uknown certificate type");
+ return 0;
+ }
+
+ /*
+ * By default, print the contents of the cert we matched so the
+ * user can decide if she is willing to accept a package from
+ * whoever signed this.
+ */
+ if (!quiet)
+ X509_print_fp(stdout, x509);
+ }
+ fclose(fp);
+
+ /*
+ * Initialize the verification contexts for both RSA and DSA.
+ */
+ if (me->has_rsa) EVP_VerifyInit(&me->rsa_ctx, EVP_sha1());
+ if (me->has_dsa) EVP_VerifyInit(&me->dsa_ctx, EVP_dss1());
+
+ return me;
+}
+
+
+/*
+ * "Add" another data block to an existing checker.
+ */
+void
+x509_add(arg, buffer, length)
+ void *arg;
+ const char *buffer;
+ size_t length;
+{
+ struct x509_checker * me = arg;
+
+ if (me->has_rsa) EVP_VerifyUpdate(&me->rsa_ctx, buffer, length);
+ if (me->has_dsa) EVP_VerifyUpdate(&me->dsa_ctx, buffer, length);
+}
+
+
+/*
+ * Finalize an existing checker and verify the signature matches one of the
+ * certs in our stack.
+ */
+int
+x509_sign_ok(arg)
+ void *arg;
+{
+ struct x509_checker * n = arg;
+ X509 * x509;
+ EVP_PKEY * pkey;
+ EVP_MD_CTX * md_ctx;
+ int status;
+
+ if (verbose)
+ printf("\n\n-------\n\nChecking package signature:\n");
+
+ while ((x509 = sk_X509_pop(n->certs)) != NULL) {
+ /*
+ * Get public key from cert.
+ */
+ pkey = X509_get_pubkey(x509);
+ if (pkey == NULL) {
+ warnx("Getting public key:");
+ ERR_print_errors_fp(stderr);
+ continue;
+ }
+
+ if (verbose)
+ X509_print_fp(stdout, x509);
+
+ switch (EVP_PKEY_type(pkey->type))
+ {
+ case EVP_PKEY_RSA:
+ md_ctx = &n->rsa_ctx;
+ break;
+
+ case EVP_PKEY_DSA:
+ md_ctx = &n->dsa_ctx;
+ break;
+
+ default:
+ break;
+ }
+
+ status = EVP_VerifyFinal(md_ctx,
+ n->signature->data,
+ n->signature->length,
+ pkey);
+
+ EVP_PKEY_free(pkey);
+ X509_free(x509);
+
+ if (status == 1) {
+ fprintf(stderr, "X.509 signature matched\n");
+
+ /*
+ * KLUDGE: Does this free the rest of the certs, or just the
+ * stack itself? Enquiring minds want to know.
+ */
+ sk_X509_free(n->certs);
+ return PKG_GOODSIG;
+ }
+ }
+
+ warnx("Verifying signature:");
+ ERR_print_errors_fp(stderr);
+ sk_X509_free(n->certs);
+ return PKG_BADSIG;
+}
+
+
+/*
+ * Sign the specified filename into sign.
+ */
+int
+retrieve_x509_marker(filename, sign, userid)
+ const char * filename;
+ struct signature ** sign;
+ const char * userid;
+{
+ struct signature * n;
+ struct mygzip_header h;
+ FILE * f, * keyf;
+ char buffer[1024];
+ ssize_t length;
+ int err;
+ int sig_len = 4096;
+ unsigned char * sig_buf;
+ EVP_MD_CTX md_ctx;
+ const EVP_MD * md_type;
+ EVP_PKEY * pkey;
+
+ char keyfile[PATH_MAX + 1] = KEYFILE;
+ char * kp;
+
+ key_from_name(keyfile, userkey);
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ free(n);
+ return 0;
+ }
+ if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) {
+ warnx("File %s is not a gzip file\n", filename);
+ fclose(f);
+ free(n);
+ return 0;
+ }
+
+ /*
+ * Sign the remaining data:
+ * Load just the crypto library error strings.
+ */
+ ERR_load_crypto_strings();
+
+ /*
+ * Read private key.
+ */
+ keyf = fopen(keyfile, "r");
+ if (keyf == NULL)
+ {
+ warnx("Cannot open private key %s.", keyfile);
+ return 0;
+ }
+
+ pkey = PEM_read_PrivateKey(keyf, NULL, NULL, 0);
+ fclose(keyf);
+
+ if (pkey == NULL)
+ {
+ warnx("Reading private key %s:", keyfile);
+ ERR_print_errors_fp(stderr);
+ return 0;
+ }
+
+ /*
+ * Do the signature. The remaining bytes of the GZIP file are the
+ * compressed tar image, which is what we are signing.
+ */
+ switch (EVP_PKEY_type(pkey->type))
+ {
+ case EVP_PKEY_RSA:
+ md_type = EVP_sha1();
+printf("*** It's an RSA key.\n");
+ break;
+
+ case EVP_PKEY_DSA:
+ md_type = EVP_dss1();
+printf("@@@ It's a DSA key, yippee!\n");
+ break;
+
+ default:
+ warnx("Uknown key type");
+ return 0;
+ }
+
+ EVP_SignInit(&md_ctx, md_type);
+
+ while ((length = fread(buffer, 1, sizeof buffer, f)) > 0)
+ EVP_SignUpdate(&md_ctx, buffer, length);
+
+ sig_buf = malloc(sig_len);
+ if (sig_buf == NULL) {
+ warnx("Cannot allocated %u bytes for signature buffer", sig_len);
+ return 0;
+ }
+
+ err = EVP_SignFinal(&md_ctx, sig_buf, &sig_len, pkey);
+
+ if (err != 1)
+ {
+ warnx("Creating signature:");
+ ERR_print_errors_fp(stderr);
+ return 0;
+ }
+
+ EVP_PKEY_free(pkey);
+
+ /*
+ * Stuff the signature onto the head of the chain of signatures in
+ * the package.
+ */
+ n = malloc(sizeof *n);
+ if (n == NULL) {
+ warnx("Cannot allocate %u bytes for new signature", sizeof *n);
+ return 0;
+ }
+ n->data = sig_buf;
+ n->length = sig_len;
+ n->type = TAG_X509;
+ memcpy(n->tag, x509tag, sizeof x509tag);
+ sign_fill_tag(n);
+ n->next = *sign;
+ *sign = n;
+
+ /*
+ * Report our success.
+ */
+ return 1;
+}
+
+
+static void
+key_from_name(char * filename, const char * ident)
+{
+ char * cp;
+
+ /*
+ * If an alternate keyfile was specified, treat it as the name of an
+ * alternate private key with which to sign or verify the package.
+ */
+ if (ident) {
+ printf("Using alternate key/cert \"%s\".\n", ident);
+ if (strchr(ident, '/')) {
+ /*
+ * The user specified a path, take it verbatim.
+ */
+ strncpy(filename, ident, PATH_MAX);
+ } else {
+ cp = dirname(filename);
+ if (cp == NULL) {
+ warnx("Key directory not correctly specified.");
+ return;
+ }
+ snprintf(filename, PATH_MAX, "%s/%s", cp, ident);
+ }
+ }
+
+ if (verbose)
+ printf("Key is \"%s\".\n", filename);
+}
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/version/Makefile b/usr.sbin/pkg_install/version/Makefile
new file mode 100644
index 0000000..b097a3a
--- /dev/null
+++ b/usr.sbin/pkg_install/version/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= pkg_version
+SRCS= main.c perform.c
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+WARNS?= 2
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+test:
+ ./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..cbc6a9e
--- /dev/null
+++ b/usr.sbin/pkg_install/version/main.c
@@ -0,0 +1,89 @@
+/*
+ * 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>
+
+static char Options[] = "dhl:L:s:tv";
+
+char *LimitChars = NULL;
+char *PreventChars = NULL;
+char *MatchName = NULL;
+
+static void usage __P((void));
+
+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 while ((ch = getopt(argc, argv, Options)) != -1) {
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'l':
+ LimitChars = optarg;
+ break;
+
+ case 'L':
+ PreventChars = optarg;
+ break;
+
+ case 's':
+ MatchName = optarg;
+ break;
+
+ case 't':
+ errx(2, "Invalid -t usage.");
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ return pkg_perform(argv);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_version [-hv] [-l limchar] [-L limchar] [-s string] index",
+ " pkg_version -t v1 v2");
+ 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..6c345b9
--- /dev/null
+++ b/usr.sbin/pkg_install/version/perform.c
@@ -0,0 +1,313 @@
+/*
+ * 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;
+struct index_head Index = SLIST_HEAD_INITIALIZER(Index);
+
+static int pkg_do(char *);
+static void show_version(const char *, 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 tmp[PATH_MAX], **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.
+ * XXX This should not be hard-coded to INDEX-5.
+ */
+ if (*indexarg == NULL)
+ snprintf(tmp, PATH_MAX, "%s/INDEX-5", PORTS_DIR);
+ else
+ strlcpy(tmp, *indexarg, PATH_MAX);
+ if (isURL(tmp))
+ IndexFile = fetchGetURL(tmp, "");
+ else
+ IndexFile = fopen(tmp, "r");
+
+ /* Get either a list of matching or all packages */
+ if (MatchName != NULL) {
+ pat[0] = MatchName;
+ pat[1] = NULL;
+ MatchType = MATCH_REGEX;
+ patterns = pat;
+ }
+ else {
+ MatchType = MATCH_ALL;
+ patterns = NULL;
+ }
+ pkgs = matchinstalled(MatchType, patterns, &err_cnt);
+
+ if (err_cnt != 0)
+ errx(2, "Unable to find package database directory!");
+ if (pkgs == NULL) {
+ switch (MatchType) {
+ case MATCH_ALL:
+ warnx("no packages installed");
+ return (0);
+ 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("unable to open %s file", CONTENTS_FNAME);
+ 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) {
+ snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin);
+ if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) {
+ if ((latest = vpipe("make -V PKGNAME", tmp)) == NULL)
+ warnx("Failed to get PKGNAME from %s/Makefile!", tmp);
+ else
+ show_version(plist.name, latest, "port");
+ }
+ }
+ if (latest == NULL) {
+ /* We only pull in the INDEX once, if needed. */
+ if (SLIST_EMPTY(&Index)) {
+ if (!IndexFile)
+ errx(2, "Unable to open INDEX in %s.", __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.name, NULL, plist.origin);
+ else
+ show_version(plist.name, 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(const char *installed, const char *latest, const char *source)
+{
+ char *ch, tmp[PATH_MAX];
+ const char *ver;
+ int cmp = 0;
+
+ if (!installed || strlen(installed) == 0)
+ return;
+ strlcpy(tmp, installed, 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 (source != NULL && OUTPUT('?')) {
+ printf("%-34s ?", tmp);
+ if (Verbose)
+ printf(" orphaned: %s", source);
+ 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 = version_of(tmp, NULL, NULL);
+ printf(" multiple versions (index has %s", ver);
+ do {
+ ver = version_of(&ch[1], NULL, NULL);
+ if ((ch = strchr(&ch[1], '|')) != NULL)
+ ch[0] = '\0';
+ printf(", %s", ver);
+ } while (ch != NULL);
+ printf(")");
+ }
+ printf("\n");
+ }
+ } else {
+ cmp = version_cmp(installed, latest);
+ ver = version_of(latest, NULL, NULL);
+ if (cmp < 0 && OUTPUT('<')) {
+ printf("%-34s <", tmp);
+ if (Verbose)
+ printf(" needs updating (%s has %s)", source, ver);
+ printf("\n");
+ } else if (cmp == 0 && OUTPUT('=')) {
+ printf("%-34s =", tmp);
+ if (Verbose)
+ printf(" up-to-date with %s", source);
+ printf("\n");
+ } else if (cmp > 0 && OUTPUT('>')) {
+ printf("%-34s >", tmp);
+ if (Verbose)
+ printf(" succeeds %s (%s has %s)", source, source, ver);
+ printf("\n");
+ }
+ }
+}
+
+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..3f03997
--- /dev/null
+++ b/usr.sbin/pkg_install/version/pkg_version.1
@@ -0,0 +1,206 @@
+.\"
+.\" 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 July 17, 1998
+.Dt PKG_VERSION 1
+.Os
+.Sh NAME
+.Nm pkg_version
+.Nd summarize installed versions of packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl hv
+.Op Fl l Ar limchar
+.Op Fl L Ar limchar
+.Op Fl s Ar string
+.Op Ar index
+.Nm
+.Op Fl t Ar version1 version2
+.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-5 ) .
+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
+Print help message.
+.It Fl l
+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
+Limit the output to those packages whose status flag doesn't 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 s
+Limit the output to those packages whose names match a given
+.Ar string .
+.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 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
+is used.
+.El
+.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
+.Sh FILES
+.Bl -tag -width /usr/ports/INDEX -compact
+.It Pa /usr/ports/INDEX
+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 ftp://ftp.FreeBSD.org/pub/FreeBSD/branches/-current/ports/INDEX
+.Pp
+The following command compares two package version strings:
+.Pp
+.Dl % pkg_version -t 1.5 1.5.1
+.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
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..0651047
--- /dev/null
+++ b/usr.sbin/pkg_install/version/test-pkg_version.sh
@@ -0,0 +1,75 @@
+#!/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
diff --git a/usr.sbin/pkg_install/version/version.h b/usr.sbin/pkg_install/version/version.h
new file mode 100644
index 0000000..daaebb0
--- /dev/null
+++ b/usr.sbin/pkg_install/version/version.h
@@ -0,0 +1,44 @@
+/* $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
+
+/* 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 we put logging information */
+#define PORTS_DIR (getenv(PORTSDIR) ? getenv(PORTSDIR) : DEF_PORTS_DIR)
+
+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;
+
+#endif /* _INST_VERSION_H_INCLUDE */
diff --git a/usr.sbin/pnpinfo/Makefile b/usr.sbin/pnpinfo/Makefile
new file mode 100644
index 0000000..c3702c8
--- /dev/null
+++ b/usr.sbin/pnpinfo/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/pnpinfo
+
+PROG= pnpinfo
+MAN= pnpinfo.8
+SRCS= pnpinfo.c
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+.if ${MACHINE_ARCH} == "alpha"
+LDADD= -lio
+DPADD= ${LIBIO}
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ppp/Makefile b/usr.sbin/ppp/Makefile
new file mode 100644
index 0000000..aebeac8
--- /dev/null
+++ b/usr.sbin/ppp/Makefile
@@ -0,0 +1,116 @@
+# $FreeBSD$
+
+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
+.if defined(RELEASE_CRUNCH)
+CFLAGS+=-DRELEASE_CRUNCH
+NOATM= true
+NODES= true
+NOI4B= true
+NONAT= true
+NOKLDLOAD= true
+NOPAM= true
+NORADIUS= true
+NOSUID= true
+.endif
+
+.if defined(NOSUID) || defined(PPP_NOSUID)
+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(NOKLDLOAD)
+CFLAGS+=-DNOKLDLOAD
+.endif
+
+.if defined(NOINET6)
+CFLAGS+=-DNOINET6
+.endif
+
+.if defined(NOALIAS) || defined(NONAT)
+CFLAGS+=-DNONAT
+.else
+SRCS+= nat_cmd.c
+LDADD+= -lalias
+DPADD+= ${LIBALIAS}
+.endif
+
+.if defined(NOATM)
+CFLAGS+=-DNOATM
+.else
+SRCS+= atm.c
+.endif
+
+.if defined(NOSUID) || defined(PPP_NOSUID)
+CFLAGS+=-DNOSUID
+.else
+SRCS+= id.c
+.endif
+
+.if defined(RELEASE_CRUNCH) || defined(NOCRYPT) || defined(NO_OPENSSL)
+CFLAGS+=-DNODES
+.else
+DISTRIBUTION=crypto
+SRCS+= chap_ms.c mppe.c
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+.endif
+
+.if defined(NORADIUS)
+CFLAGS+=-DNORADIUS
+.else
+SRCS+= radius.c
+LDADD+= -lradius
+DPADD+= ${LIBRADIUS}
+.endif
+
+.if defined(NOI4B) || ${MACHINE_ARCH} != "i386"
+CFLAGS+=-DNOI4B
+.else
+SRCS+= i4b.c
+.endif
+
+.if defined(NONETGRAPH)
+CFLAGS+=-DNONETGRAPH
+.else
+SRCS+= ether.c
+LDADD+= -lnetgraph
+DPADD+= ${LIBNETGRAPH}
+.if defined(EXPERIMENTAL_NETGRAPH)
+CFLAGS+=-DEXPERIMENTAL_NETGRAPH
+SRCS+= netgraph.c
+.endif
+.endif
+
+.if defined(NOPAM)
+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..5b8a7cd
--- /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 cuaa1, then cuaa0
+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.devel b/usr.sbin/ppp/README.devel
new file mode 100644
index 0000000..318cd46
--- /dev/null
+++ b/usr.sbin/ppp/README.devel
@@ -0,0 +1,49 @@
+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
+This program was originally written by Toshiharu OHNO <tony-o@iij.ad.jp>,
+and was submitted to FreeBSD-2.0.5 by Atsushi Murai <amurai@spec.co.jp>.
+The original version was usually referred to as iij-ppp.
+
+Ppp is currently maintained under FreeBSD and OpenBSD by Brian Somers
+<brian@Awfulhak.org>. The sources for both operating systems are the
+same except that the libalias code is built directly into ppp under
+OpenBSD, and the Makefiles vary per OS.
+
+The latest sources are available in FreeBSD-current and OpenBSD-current.
+An archive hacked so that it will build on just about any version of
+FreeBSD or OpenBSD is frequently generated and made available via
+http://www.Awfulhak.org/ppp.html.
+
+A FAQ is available at
+http://www.freebsd.org/doc/en_US.ISO8859-1/books/faq/ppp.html. It
+applies equally to OpenBSD as it does to FreeBSD. The man page is
+quite extensive, and there are lots of examples in
+/usr/share/examples/ppp/.
+
+Ppp is still under development. There is no official TODO list.
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..4db0b6b
--- /dev/null
+++ b/usr.sbin/ppp/acf.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 "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, struct link *l, struct mbuf *bp,
+ int pri, 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, struct link *l, struct mbuf *bp, u_short *proto)
+{
+ 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.SaveInErrors++;
+ 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.SaveInErrors++;
+ 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..2c51352
--- /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 s)
+{
+ 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(s, 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, int s)
+{
+ return (arp_ProxySub(bundle, addr, 1, s));
+}
+
+/*
+ * arp_ClearProxy - Delete the proxy ARP entry for the peer.
+ */
+int
+arp_ClearProxy(struct bundle *bundle, struct in_addr addr, int s)
+{
+ return (arp_ProxySub(bundle, addr, 0, s));
+}
+
+#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(s, 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(int s, 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..ac0a1ca
--- /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, int);
+extern int arp_SetProxy(struct bundle *, struct in_addr, int);
+extern int arp_EtherAddr(int, 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..0fcf865
--- /dev/null
+++ b/usr.sbin/ppp/async.c
@@ -0,0 +1,216 @@
+/*-
+ * 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 *bundle, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ u_char *cp, *sp, *ep;
+ struct mbuf *wp;
+ int cnt;
+
+ if (!p || m_length(bp) > HDLCSIZE) {
+ m_freem(bp);
+ return NULL;
+ }
+
+ 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);
+ 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, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ 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..0d51564
--- /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)
+
+int
+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, int *auxfd, int *nauxfd)
+{
+ 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, int *auxfd, int *nauxfd)
+{
+ 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..3fbc0c5
--- /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 int atm_DeviceSize(void);
diff --git a/usr.sbin/ppp/auth.c b/usr.sbin/ppp/auth.c
new file mode 100644
index 0000000..54f0c1c
--- /dev/null
+++ b/usr.sbin/ppp/auth.c
@@ -0,0 +1,481 @@
+/*-
+ * 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, struct physical *physical)
+{
+ /* 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(struct bundle *bundle, const char *name, int len,
+ struct physical *physical)
+{
+ /* 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)
+{
+ int 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 (%d > %d) !\n",
+ ntohs(authp->in.hdr.length), len);
+ } else {
+ authp->in.hdr.length = htons(0);
+ log_Printf(LogWARN, "auth_ReadHeader: Short packet header (%d > %d) !\n",
+ (int)(sizeof authp->in.hdr), len);
+ }
+
+ m_freem(bp);
+ return NULL;
+}
+
+struct mbuf *
+auth_ReadName(struct authinfo *authp, struct mbuf *bp, int len)
+{
+ if (len > sizeof authp->in.name - 1)
+ log_Printf(LogWARN, "auth_ReadName: Name too long (%d) !\n", len);
+ else {
+ int mlen = m_length(bp);
+
+ if (len > mlen)
+ log_Printf(LogWARN, "auth_ReadName: Short packet (%d > %d) !\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..bbc257c
--- /dev/null
+++ b/usr.sbin/ppp/auth.h
@@ -0,0 +1,70 @@
+/*-
+ * 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 *,
+ struct physical *);
+extern char *auth_GetSecret(struct bundle *, const char *, int,
+ struct physical *);
+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 *, int);
diff --git a/usr.sbin/ppp/bundle.c b/usr.sbin/ppp/bundle.c
new file mode 100644
index 0000000..be55a6a
--- /dev/null
+++ b/usr.sbin/ppp/bundle.c
@@ -0,0 +1,2009 @@
+/*-
+ * 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, struct fsm *fp)
+{
+ /* 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, 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, 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.opt = OPT_IDCHECK | OPT_LOOPBACK | OPT_SROUTES | OPT_TCPMSSFIXUP |
+ OPT_THROUGHPUT | OPT_UTMP;
+#ifndef NOINET6
+ bundle.cfg.opt |= OPT_IPCP;
+ if (probe.ipv6_available)
+ bundle.cfg.opt |= 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 bit)
+{
+ return (bundle->cfg.opt & bit) ? "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: %ds\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, "%ds", arg->bundle->cfg.idle.timeout);
+ if (arg->bundle->cfg.idle.min_timeout)
+ prompt_Printf(arg->prompt, ", min %ds",
+ 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));
+
+ 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) {
+ int up = now - bundle->upat;
+
+ if ((long long)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, int timeout, int min_timeout)
+{
+ bundle->cfg.idle.timeout = timeout;
+ if (min_timeout >= 0)
+ 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, 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)) != iov[0].iov_len) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed recvmsg: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed recvmsg: Got %d, 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 %d, 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 %d, 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], 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 != iov[0].iov_len)
+ log_Printf(LogERROR, "%s: Failed initial sendmsg: Only sent %d 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 %d 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 %d 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;
+ }
+}
+
+int
+bundle_HighestState(struct bundle *bundle)
+{
+ struct datalink *dl;
+ int 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 %d to %d (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..d27d3b0
--- /dev/null
+++ b/usr.sbin/ppp/bundle.h
@@ -0,0 +1,211 @@
+/*-
+ * 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 0x0001
+#define OPT_FORCE_SCRIPTS 0x0002 /* force chat scripts */
+#define OPT_IDCHECK 0x0004
+#define OPT_IFACEALIAS 0x0008
+#ifndef NOINET6
+#define OPT_IPCP 0x0010
+#define OPT_IPV6CP 0x0020
+#endif
+#define OPT_KEEPSESSION 0x0040
+#define OPT_LOOPBACK 0x0080
+#define OPT_PASSWDAUTH 0x0100
+#define OPT_PROXY 0x0200
+#define OPT_PROXYALL 0x0400
+#define OPT_SROUTES 0x0800
+#define OPT_TCPMSSFIXUP 0x1000
+#define OPT_THROUGHPUT 0x2000
+#define OPT_UTMP 0x4000
+
+#define MAX_ENDDISC_CLASS 5
+
+#define Enabled(b, o) ((b)->cfg.opt & (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 {
+ int timeout; /* NCP Idle timeout value */
+ int 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 opt; /* Uses OPT_ bits from above */
+ char label[50]; /* last thing `load'ed */
+ u_short ifqueue; /* Interface queue size */
+
+ struct {
+ int 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 *, int, int);
+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 int 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..674ea70
--- /dev/null
+++ b/usr.sbin/ppp/cbcp.c
@@ -0,0 +1,759 @@
+/*-
+ * 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(int 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(int 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[1]; /* Really 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;
+ strcpy(addr->addr, next);
+ 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;
+ strcpy(addr->addr, cbcp->fsm.phone);
+ 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 {
+ strcpy(cbcp->fsm.phone, addr->addr);
+ 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;
+ strcpy(addr->addr, cbcp->fsm.phone);
+ 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, 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;
+ int 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 %d not %d)"
+ " - 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 %d)\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..78ea13a
--- /dev/null
+++ b/usr.sbin/ppp/ccp.c
@@ -0,0 +1,819 @@
+/*-
+ * 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 || 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)
+{
+ int 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];
+ int f, 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 == f)
+ break;
+
+ if (alloc || *o == NULL) {
+ *o = (struct ccp_opt *)malloc(sizeof(struct ccp_opt));
+ (*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)
+{
+ /* 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;
+ int 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 != f || ccp->out.algorithm != 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 < 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;
+ for (f = 0; f < 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 < 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 = 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, 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, 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)
+{
+ return 1;
+}
+
+int
+ccp_DefaultRequired(struct fsm *fp)
+{
+ 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..305e5c7
--- /dev/null
+++ b/usr.sbin/ppp/chap.c
@@ -0,0 +1,967 @@
+/*-
+ * 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 <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, u_char type
+#ifndef NODES
+ , 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;
+ int 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;
+ int 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, nlen,
+ 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, nlen,
+ 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, u_char type
+#ifndef NODES
+ , int lm
+#endif
+ )
+{
+ u_char *ans;
+
+ ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer, type
+#ifndef NODES
+ , 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, fd_set *e, 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, const fd_set *fdset)
+{
+ 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, chap->auth.physical->link.lcp.his_authtype
+#ifndef NODES
+ , lanman
+#endif
+ );
+ chap_Cleanup(chap, 0);
+ }
+ }
+}
+
+static int
+chap_Write(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ /* 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(u_char type, char *myans, int mylen, char *hisans, int hislen
+#ifndef NODES
+ , 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, 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),
+ p->link.lcp.his_authtype
+#ifndef NODES
+ , 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(bundle, name, nlen, p);
+ 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,
+ p->link.lcp.want_authtype
+#ifndef NODES
+ , chap->challenge.peer,
+ chap->authresponse, lanman);
+ MPPE_IsServer = 1; /* XXX Global ! */
+#else
+ );
+#endif
+ if (myans == NULL)
+ key = NULL;
+ else {
+ if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
+ ans + 1, alen
+#ifndef NODES
+ , 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..617555d
--- /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 - (int)&((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..74ac199
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.c
@@ -0,0 +1,417 @@
+/*-
+ * 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);
+}
+
+void
+ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge,
+ char *UserName, int UserNameLen, 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, int UserNameLen, char *Password,
+ int PasswordLen, char *Response)
+{
+ char Challenge[8];
+ char PasswordHash[16];
+
+ ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, UserNameLen,
+ 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,
+ int UserNameLen, 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, UserNameLen,
+ 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..35ee978
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.h
@@ -0,0 +1,53 @@
+/*-
+ * 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 *, int, char *, int , char *);
+extern void HashNtPasswordHash(char *, char *);
+extern void NtPasswordHash(char *, int, char *);
+extern void ChallengeHash(char *, char *, char *UserName, int, char *);
+extern void GenerateAuthenticatorResponse(char *, int, char *, char *, char *, char *, int, 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..79e41ea
--- /dev/null
+++ b/usr.sbin/ppp/chat.c
@@ -0,0 +1,794 @@
+/*-
+ * 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;
+ c->abort.string[i].data = (char *)malloc(len+1);
+ 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, const fd_set *fdset)
+{
+ 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 > 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, const fd_set *fdset)
+{
+ 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..635b197
--- /dev/null
+++ b/usr.sbin/ppp/command.c
@@ -0,0 +1,3203 @@
+/*-
+ * 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
+
+/* ``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_ENDDISC 46
+#define NEG_LQR 47
+#define NEG_PAP 48
+#define NEG_PPPDDEFLATE 49
+#define NEG_PRED1 50
+#define NEG_PROTOCOMP 51
+#define NEG_SHORTSEQ 52
+#define NEG_VJCOMP 53
+#define NEG_MPPE 54
+#define NEG_CHAP81 55
+
+const char Version[] = "3.1";
+
+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
+
+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 >= 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)
+{
+ 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 - loldstr);
+ 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] = subst(nargv[arg], "COMPILATIONDATE", __DATE__);
+ 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 >= 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]"},
+ {"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"},
+ {"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]"},
+ {"proto", NULL, nat_RedirectProto, LOCAL_AUTH, "protocol redirection",
+ "nat proto proto localIP [publicIP [remoteIP]]"},
+ {"proxy", NULL, nat_ProxyRule, LOCAL_AUTH,
+ "proxy control", "nat proxy server host[:port] ..."},
+#ifndef NO_FW_PUNCH
+ {"punch_fw", NULL, nat_PunchFW, LOCAL_AUTH,
+ "firewall control", "nat punch_fw [base count]"},
+#endif
+ {"skinny_port", NULL, nat_SkinnyPort, LOCAL_AUTH,
+ "TCP port used by Skinny Station protocol", "nat skinny_port [port]"},
+ {"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" },
+ {"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},
+};
+#endif
+
+static struct cmdtab const AllowCommands[] = {
+ {"modes", "mode", AllowModes, LOCAL_AUTH,
+ "Only allow certain ppp modes", "allow modes mode..."},
+ {"users", "user", AllowUsers, LOCAL_AUTH,
+ "Only allow ppp access to certain users", "allow users logname..."},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "allow help|? [command]", AllowCommands},
+ {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]"},
+ {"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"},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "nat help|? [command]", IfaceCommands},
+ {NULL, NULL, NULL},
+};
+
+static struct cmdtab const Commands[] = {
+ {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "accept option request", "accept option .."},
+ {"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"},
+ {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Clear throughput statistics",
+ "clear ipcp|ipv6cp|physical [current|overall|peak]..."},
+ {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
+ "Clone a link", "clone newname..."},
+ {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Close an FSM", "close [lcp|ccp]"},
+ {"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 .."},
+ {"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 .."},
+ {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Generate a down event", "down [ccp|lcp]"},
+ {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Enable option", "enable option .."},
+ {"ident", NULL, IdentCommand, LOCAL_AUTH | LOCAL_CX,
+ "Set the link identity", "ident text..."},
+ {"iface", "interface", RunListCommand, LOCAL_AUTH,
+ "interface control", "iface option ...", IfaceCommands},
+ {"link", "datalink", LinkCommand, LOCAL_AUTH,
+ "Link specific commands", "link name command ..."},
+ {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Load settings", "load [system ...]"},
+ {"log", NULL, LogCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "log information", "log word ..."},
+#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"},
+ {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Quit PPP program", "quit|bye [all]"},
+ {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
+ "Remove a link", "remove"},
+ {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
+ "Rename a link", "rename name"},
+ {"resolv", NULL, ResolvCommand, LOCAL_AUTH,
+ "Manipulate resolv.conf", "resolv readonly|reload|restore|rewrite|writable"},
+ {"save", NULL, SaveCommand, LOCAL_AUTH,
+ "Save settings", "save"},
+ {"sendident", NULL, SendIdentification, LOCAL_AUTH | LOCAL_CX,
+ "Transmit the link identity", "sendident"},
+ {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Set parameters", "set[up] var value"},
+ {"shell", "!", FgShellCommand, LOCAL_AUTH,
+ "Run a subshell", "shell|! [sh command]"},
+ {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Show status and stats", "show var"},
+ {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
+ "Enter terminal mode", "term"},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "help|? [command]", Commands},
+ {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 - %s\n", Version, __DATE__);
+ 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"},
+ {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
+ "CCP status", "show cpp"},
+ {"compress", NULL, sl_Show, LOCAL_AUTH,
+ "VJ compression stats", "show compress"},
+ {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
+ "escape characters", "show escape"},
+ {"filter", NULL, filter_Show, LOCAL_AUTH,
+ "packet filters", "show filter [in|out|dial|alive]"},
+ {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
+ "HDLC errors", "show hdlc"},
+ {"iface", "interface", iface_Show, LOCAL_AUTH,
+ "Interface status", "show iface"},
+ {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
+ "IPCP status", "show ipcp"},
+#ifndef NOINET6
+ {"ipv6cp", NULL, ipv6cp_Show, LOCAL_AUTH,
+ "IPV6CP status", "show ipv6cp"},
+#endif
+ {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Protocol layers", "show layers"},
+ {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
+ "LCP status", "show lcp"},
+ {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
+ "(high-level) link info", "show link"},
+ {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
+ "available link names", "show links"},
+ {"log", NULL, log_ShowLevel, LOCAL_AUTH,
+ "log levels", "show log"},
+ {"mem", NULL, mbuf_Show, LOCAL_AUTH,
+ "mbuf allocations", "show mem"},
+ {"ncp", NULL, ncp_Show, LOCAL_AUTH,
+ "NCP status", "show ncp"},
+ {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX,
+ "(low-level) link info", "show physical"},
+ {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
+ "multilink setup", "show mp"},
+ {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
+ "protocol summary", "show proto"},
+ {"route", NULL, route_Show, LOCAL_AUTH,
+ "routing table", "show route"},
+ {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
+ "STOPPED timeout", "show stopped"},
+ {"timers", NULL, ShowTimerList, LOCAL_AUTH,
+ "alarm timers", "show timers"},
+ {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "version string", "show version"},
+ {"who", NULL, log_ShowWho, LOCAL_AUTH,
+ "client list", "show who"},
+ {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "Display this message", "show help|? [command]", ShowCommands},
+ {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(int argc, 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, 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(argc, 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(EX_NORMAL);
+ 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) {
+ 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;
+ int 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) */
+ ncpaddr_getip4(&ncpaddr, &ncp->ipcp.my_ip);
+ ncprange_getaddr(&ncp->ipcp.cfg.my_range, &ncpaddr);
+ 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 {
+ int timeout, min;
+
+ timeout = atoi(argp);
+ min = arg->argc == arg->argn + 2 ? atoi(arg->argv[arg->argn + 1]) : -1;
+ bundle_SetIdleTimer(arg->bundle, timeout, min);
+ }
+ break;
+
+ 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;
+ }
+
+ 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"},
+ {"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]"},
+ {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
+ "escape characters", "set escape hex-digit ..."},
+ {"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]]"},
+ {"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]]]]"},
+ {"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|sync|tcp/ip|timer|tun..."},
+ {"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", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "LQR period", "set lqrperiod 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]"},
+#ifndef NORADIUS
+ {"radius", NULL, SetVariable, LOCAL_AUTH,
+ "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS},
+#endif
+ {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
+ "Reconnect timeout", "set reconnect value ntries"},
+ {"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]"},
+ {"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]]"},
+ {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
+ "physical speed", "set speed value|sync"},
+ {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
+ "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
+ {"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]"},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "set help|? [command]", SetCommands},
+ {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)
+ PacketAliasSetAddress(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;
+ arg->bundle->cfg.opt &= ~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) {
+ PacketAliasSetMode(param, param);
+ return 0;
+ }
+ log_Printf(LogWARN, "nat not enabled\n");
+ } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
+ if (arg->bundle->NatEnabled) {
+ PacketAliasSetMode(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 bit = (int)(long)arg->cmd->args;
+ unsigned keep; /* Keep these bits */
+ unsigned add; /* Add these bits */
+
+ if (ident_cmd(arg->argv[arg->argn - 2], &keep, &add) == NULL)
+ return 1;
+
+#ifndef NOINET6
+ if (add == NEG_ENABLED && bit == OPT_IPV6CP && !probe.ipv6_available) {
+ log_Printf(LogWARN, "IPv6 is not available on this machine\n");
+ return 1;
+ }
+#endif
+
+ if (add)
+ arg->bundle->cfg.opt |= bit;
+ else
+ arg->bundle->cfg.opt &= ~bit;
+
+ return 0;
+}
+
+static int
+IfaceAliasOptSet(struct cmdargs const *arg)
+{
+ unsigned save = arg->bundle->cfg.opt;
+ int result = OptSet(arg);
+
+ if (result == 0)
+ if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->NatEnabled) {
+ arg->bundle->cfg.opt = 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_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[] = {
+ {"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},
+ {"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 OPT_MAX 14 /* accept/deny allowed below and not above */
+#else
+#define OPT_MAX 12
+#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},
+};
+
+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 : 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 (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..085e9c4
--- /dev/null
+++ b/usr.sbin/ppp/datalink.c
@@ -0,0 +1,1470 @@
+/*-
+ * 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 *, int);
+
+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, dl->bundle) >= 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 < 0 || dl->state >= sizeof states / sizeof states[0])
+ return "unknown";
+ return states[dl->state];
+}
+
+static void
+datalink_NewState(struct datalink *dl, int state)
+{
+ if (state != dl->state) {
+ if (state >= 0 && 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;
+
+ 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 (oname)
+ free(datalink_NextName(dl));
+ else
+ oname = datalink_NextName(dl);
+ 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);
+}
+
+char *
+datalink_NextName(struct datalink *dl)
+{
+ int f, n;
+ char *name, *oname;
+
+ n = strlen(dl->name);
+ name = (char *)malloc(n+3);
+ 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..64b3c16
--- /dev/null
+++ b/usr.sbin/ppp/datalink.h
@@ -0,0 +1,157 @@
+/*-
+ * 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 */
+ int 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 char *datalink_NextName(struct datalink *);
+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..3e6198d
--- /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, int pri, 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, 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, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ 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, 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, 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, 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..f29eff6
--- /dev/null
+++ b/usr.sbin/ppp/defs.c
@@ -0,0 +1,443 @@
+/*-
+ * 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 {
+ int 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 }
+};
+
+int
+SpeedToInt(speed_t speed)
+{
+ const struct speeds *sp;
+
+ for (sp = speeds; sp->nspeed; sp++) {
+ if (sp->speed == speed) {
+ return sp->nspeed;
+ }
+ }
+ return 0;
+}
+
+speed_t
+IntToSpeed(int 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 < 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, 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;
+ }
+}
+
+int
+loadmodules(int how, const char *module, ...)
+{
+ int loaded = 0;
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ 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);
+#endif
+ return loaded;
+}
diff --git a/usr.sbin/ppp/defs.h b/usr.sbin/ppp/defs.h
new file mode 100644
index 0000000..2be7686
--- /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/cuaa1\0/dev/cuaa0" /* 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 int SpeedToInt(speed_t);
+extern speed_t IntToSpeed(int);
+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..77ad7e5
--- /dev/null
+++ b/usr.sbin/ppp/ether.c
@@ -0,0 +1,722 @@
+/*-
+ * 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)
+
+int
+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 : 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, 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, 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;
+ int ifacelen, 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:", 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);
+
+ /* 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;
+
+ snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook);
+ 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..16c9a9e
--- /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 int ether_DeviceSize(void);
diff --git a/usr.sbin/ppp/exec.c b/usr.sbin/ppp/exec.c
new file mode 100644
index 0000000..67d00ac
--- /dev/null
+++ b/usr.sbin/ppp/exec.c
@@ -0,0 +1,233 @@
+/*-
+ * 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, int *auxfd, int *nauxfd)
+{
+ 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..994c090
--- /dev/null
+++ b/usr.sbin/ppp/filter.c
@@ -0,0 +1,606 @@
+/*-
+ * 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 int 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, const struct protoent *pe,
+ 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, char const * const *argv, const struct protoent *pe,
+ 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, pe, &fe);
+ break;
+ default:
+ val = ParseGeneric(argc, argv, pe, &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(int act)
+{
+ static const char * const actname[] = { " none ", "permit ", " deny " };
+ static char buf[8];
+
+ if (act >= 0 && 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(int op)
+{
+ if (op >= sizeof opname / sizeof opname[0])
+ return "unknown";
+ return opname[op];
+
+}
+
+static int
+filter_Nam2Op(const char *cp)
+{
+ int 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..f994f4b
--- /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(int);
+extern const char *filter_Op2Nam(int);
+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..685e5f1
--- /dev/null
+++ b/usr.sbin/ppp/fsm.c
@@ -0,0 +1,1209 @@
+/*-
+ * 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, int 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 < 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 < 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 < 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, 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;
+ int plen, flen;
+ u_char *cp;
+
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (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 < 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, struct fsmheader *lhp, struct mbuf *bp)
+{
+ m_freem(bp);
+}
+
+static void
+FsmRecvProtoRej(struct fsm *fp, struct fsmheader *lhp, 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, struct mbuf *bp)
+{
+ if (fsm2lcp(fp))
+ bp = lqr_RecvEcho(fp, bp);
+
+ m_freem(bp);
+}
+
+static void
+FsmRecvDiscReq(struct fsm *fp, struct fsmheader *lhp, 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, struct fsmheader *lhp, 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)
+{
+ int 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 %d 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)
+{
+ 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)
+{
+ int cplen = o->hdr.len;
+
+ if (optlen < sizeof(struct fsm_opt_hdr))
+ optlen = 0;
+
+ if (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..3280ba0
--- /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) */
+ int 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;
+};
+
+#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 *, int, 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..27077dc
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.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/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);
+}
+
+int
+hdlc_WrapperOctets(struct lcp *lcp, u_short proto)
+{
+ return 2;
+}
+
+static struct mbuf *
+hdlc_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ 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)
+{
+ int 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, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ 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);
+
+ fcs = hdlc_Fcs(MBUF_CTOP(bp), bp->m_len);
+
+ log_Printf(LogDEBUG, "%s: hdlc_LayerPull: fcs = %04x (%s)\n",
+ p->link.name, fcs, (fcs == GOODFCS) ? "good" : "BAD!");
+
+ if (fcs != GOODFCS) {
+ p->hdlc.lqm.SaveInErrors++;
+ p->hdlc.stats.badfcs++;
+ m_freem(bp);
+ return NULL;
+ }
+
+ p->hdlc.lqm.SaveInOctets += bp->m_len + 1;
+ p->hdlc.lqm.SaveInPackets++;
+
+ len = m_length(bp);
+ 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, int 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..71f0814
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.h
@@ -0,0 +1,111 @@
+/*-
+ * 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 OutPackets; /* Packets sent by me */
+ u_int32_t OutOctets; /* Octets sent by me */
+ u_int32_t SaveInPackets; /* Packets received from peer */
+ u_int32_t SaveInDiscards; /* Discards */
+ u_int32_t SaveInErrors; /* Errors */
+ u_int32_t SaveInOctets; /* Octets received from peer */
+
+ struct {
+ u_int32_t OutLQRs; /* LQRs sent by me */
+ u_int32_t SaveInLQRs; /* LQRs received from peer */
+ 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 **, int, int);
+extern int hdlc_WrapperOctets(struct lcp *, u_short);
+
+extern struct layer hdlclayer;
diff --git a/usr.sbin/ppp/i4b.c b/usr.sbin/ppp/i4b.c
new file mode 100644
index 0000000..5744e89
--- /dev/null
+++ b/usr.sbin/ppp/i4b.c
@@ -0,0 +1,451 @@
+/*-
+ * 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 <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef __FreeBSD__
+#include <machine/i4b_ioctl.h>
+#include <machine/i4b_rbch_ioctl.h>
+#else
+#ifdef __NetBSD__
+#include <netisdn/i4b_ioctl.h>
+#include <netisdn/i4b_rbch_ioctl.h>
+#else
+#include <i4b/i4b_ioctl.h>
+#include <i4b/i4b_rbch_ioctl.h>
+#endif
+#endif
+#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 "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "main.h"
+#include "i4b.h"
+
+#define Online(dev) ((dev)->mbits & TIOCM_CD)
+
+struct i4bdevice {
+ 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* */
+};
+
+#define device2i4b(d) ((d)->type == I4B_DEVICE ? (struct i4bdevice *)d : NULL)
+
+int
+i4b_DeviceSize(void)
+{
+ return sizeof(struct i4bdevice);
+}
+
+/*
+ * i4b_Timeout() watches the DCD signal and mentions it if it's status
+ * changes.
+ */
+static void
+i4b_Timeout(void *data)
+{
+ struct physical *p = data;
+ struct i4bdevice *dev = device2i4b(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) {
+ log_Printf(LogPHASE, "%s: ioctl error (%s)!\n", p->link.name,
+ strerror(errno));
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ timer_Stop(&dev->Timer);
+ 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) {
+ log_Printf(LogPHASE, "%s: %s: No carrier"
+ " (increase ``set cd'' from %d ?)\n",
+ p->link.name, p->name.full, dev->dev.cd.delay);
+ timer_Stop(&dev->Timer);
+ /* i4b_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
+i4b_StartTimer(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ timer_Stop(&dev->Timer);
+ dev->Timer.load = SECTICKS;
+ dev->Timer.func = i4b_Timeout;
+ dev->Timer.name = "i4b CD";
+ dev->Timer.arg = p;
+ log_Printf(LogDEBUG, "%s: Using i4b_Timeout [%p]\n",
+ p->link.name, i4b_Timeout);
+ timer_Start(&dev->Timer);
+}
+
+static int
+i4b_AwaitCarrier(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ if (dev->mbits == -1) {
+ if (dev->Timer.state == TIMER_STOPPED) {
+ dev->carrier_seconds = 0;
+ i4b_StartTimer(p);
+ }
+ return CARRIER_PENDING; /* Not yet ! */
+ }
+
+ return Online(dev) ? CARRIER_OK : CARRIER_LOST;
+}
+
+static int
+i4b_Raw(struct physical *p)
+{
+ int oldflag;
+
+ log_Printf(LogDEBUG, "%s: Entering i4b_Raw\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
+i4b_Offline(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ if (p->fd >= 0) {
+ timer_Stop(&dev->Timer);
+ if (Online(dev)) {
+ int dummy;
+
+ dummy = 1;
+ ioctl(p->fd, TIOCCDTR, &dummy);
+ }
+ }
+}
+
+static void
+i4b_Cooked(struct physical *p)
+{
+ int oldflag;
+
+ i4b_Offline(p); /* In case of emergency close()s */
+
+ if ((oldflag = fcntl(p->fd, F_GETFL, 0)) != -1)
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+}
+
+static void
+i4b_StopTimer(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ timer_Stop(&dev->Timer);
+}
+
+static void
+i4b_Free(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+
+ i4b_Offline(p); /* In case of emergency close()s */
+ free(dev);
+}
+
+static int
+i4b_Speed(struct physical *p)
+{
+ struct termios ios;
+ int ret;
+
+ if (tcgetattr(p->fd, &ios) == -1 ||
+ (ret = SpeedToInt(cfgetispeed(&ios))) == 0)
+ ret = 64000;
+
+ return ret;
+}
+
+static const char *
+i4b_OpenInfo(struct physical *p)
+{
+ struct i4bdevice *dev = device2i4b(p->handler);
+ static char buf[26];
+
+ if (Online(dev))
+ snprintf(buf, sizeof buf, "carrier took %ds", dev->carrier_seconds);
+ else
+ *buf = '\0';
+
+ return buf;
+}
+
+static int
+i4b_Slot(struct physical *p)
+{
+ struct stat st;
+
+ if (fstat(p->fd, &st) == 0)
+ return minor(st.st_rdev);
+
+ return -1;
+}
+
+static void
+i4b_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ struct i4bdevice *dev = device2i4b(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->Timer.state != TIMER_STOPPED) {
+ timer_Stop(&dev->Timer);
+ dev->Timer.state = TIMER_RUNNING;
+ }
+}
+
+static struct device basei4bdevice = {
+ I4B_DEVICE,
+ "i4b",
+ 0,
+ { CD_REQUIRED, DEF_I4BCDDELAY },
+ i4b_AwaitCarrier,
+ NULL,
+ i4b_Raw,
+ i4b_Offline,
+ i4b_Cooked,
+ NULL,
+ i4b_StopTimer,
+ i4b_Free,
+ NULL,
+ NULL,
+ i4b_device2iov,
+ i4b_Speed,
+ i4b_OpenInfo,
+ i4b_Slot
+};
+
+struct device *
+i4b_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov, int *auxfd, int *nauxfd)
+{
+ if (type == I4B_DEVICE) {
+ struct i4bdevice *dev = (struct i4bdevice *)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, &basei4bdevice, 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 */
+ i4b_StartTimer(p);
+ }
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+struct device *
+i4b_Create(struct physical *p)
+{
+ struct i4bdevice *dev;
+ int oldflag, dial;
+ msg_vr_req_t req;
+ telno_t number;
+
+ if (p->fd < 0 || ioctl(p->fd, I4B_RBCH_VR_REQ, &req))
+ /* Don't want this */
+ return NULL;
+
+ /*
+ * We don't bother validating the version.... all versions of i4b that
+ * support I4B_RBCH_VR_REQ are fair game :-)
+ */
+
+ if (*p->name.full == '\0') {
+ physical_SetDevice(p, ttyname(p->fd));
+ log_Printf(LogDEBUG, "%s: Input is an i4b version %d.%d.%d isdn "
+ "device (%s)\n", p->link.name, req.version, req.release,
+ req.step, p->name.full);
+ dial = 0;
+ } else {
+ log_Printf(LogDEBUG, "%s: Opened %s (i4b version %d.%d.%d)\n",
+ p->link.name, p->name.full, req.version, req.release, req.step);
+ dial = 1;
+ }
+
+ /* We're gonna return an i4bdevice (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, &basei4bdevice, sizeof dev->dev);
+ memset(&dev->Timer, '\0', sizeof dev->Timer);
+ dev->mbits = -1;
+
+ 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;
+ }
+
+ 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));
+ i4b_Cooked(p);
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+ return NULL;
+ } else
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+
+ if (dial) {
+ strncpy(number, datalink_ChoosePhoneNumber(p->dl), sizeof number - 1);
+ number[sizeof number - 1] = '\0';
+ if (number[0] == '\0')
+ dial = 0;
+ }
+ if (dial && ioctl(p->fd, I4B_RBCH_DIALOUT, number) == -1) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+
+ log_Printf(LogWARN, "%s: ioctl(I4B_RBCH_DIALOUT): %s\n",
+ p->link.name, strerror(errno));
+ i4b_Cooked(p);
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+ return NULL;
+ }
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC);
+
+ return &dev->dev;
+}
diff --git a/usr.sbin/ppp/i4b.h b/usr.sbin/ppp/i4b.h
new file mode 100644
index 0000000..984535c
--- /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 int 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..409da49
--- /dev/null
+++ b/usr.sbin/ppp/id.h
@@ -0,0 +1,91 @@
+/*-
+ * 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)
+#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..8a23742
--- /dev/null
+++ b/usr.sbin/ppp/iface.c
@@ -0,0 +1,719 @@
+/*-
+ * 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, n, s4 = -1, s6 = -1, *s;
+
+ 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, n, removed, s;
+ 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;
+ int n, 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;
+ }
+ new_flags = (ifrq.ifr_flags & 0xffff) | (ifrq.ifr_flagshigh << 16);
+
+ if (how == IFACE_ADDFLAGS)
+ new_flags |= flags;
+ else
+ new_flags &= ~flags;
+ ifrq.ifr_flags = new_flags & 0xffff;
+ ifrq.ifr_flagshigh = new_flags >> 16;
+
+ 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;
+ int f, 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 %d 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..28c3761
--- /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_*) */
+ int mtu; /* struct tuninfo MTU */
+
+ int 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..9c5a98b
--- /dev/null
+++ b/usr.sbin/ppp/ip.c
@@ -0,0 +1,985 @@
+/*-
+ * 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, "*" }
+ };
+ int 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, "*" }
+ };
+ int 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, u_int32_t family,
+ 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;
+ int 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 >= sizeof namewithdot)
+ end = ptr + sizeof namewithdot - 1;
+ while (ptr < end) {
+ len = *ptr++;
+ if (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)
+{
+ 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, loglen, result;
+ char logbuf[200];
+ int datalen, frag;
+ 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);
+ 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 int
+ip_Input(struct bundle *bundle, struct link *l, struct mbuf *bp, u_int32_t af)
+{
+ int nb, nw;
+ 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 %d, 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 != nb) {
+ if (nw == -1)
+ log_Printf(LogERROR, "ip_Input: %s: wrote %d, got %s\n",
+ l->name, nb, strerror(errno));
+ else
+ log_Printf(LogERROR, "ip_Input: %s: wrote %d, got %d\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..7d8ae63
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.c
@@ -0,0 +1,1476 @@
+/*-
+ * 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 *);
+
+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 */
+ };
+ int 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;
+
+ 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++;
+ }
+ 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;
+ size_t 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 %lu of %lu\n",
+ _PATH_RESCONF, (unsigned long)got, (unsigned 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, 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, int),
+ const struct iface_addr *addr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct in_addr peer, mask, ip;
+ int n, ret, s;
+
+ if (!ncpaddr_getip4(&addr->peer, &peer)) {
+ log_Printf(LogERROR, "Oops, ipcp_proxyarp() called with unexpected addr\n");
+ return 0;
+ }
+
+ if ((s = ID0socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
+ log_Printf(LogERROR, "ipcp_proxyarp: socket: %s\n",
+ strerror(errno));
+ 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, s)))
+ break;
+ n--;
+ }
+ ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
+ }
+ ret = !n;
+ } else if (Enabled(bundle, OPT_PROXY))
+ ret = (*proxyfun)(bundle, peer, s);
+
+ close(s);
+
+ 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[24];
+ 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)
+{
+ /* 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_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);
+#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)
+ PacketAliasSetAddress(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);
+#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;
+ int 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 >= 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:
+ pcomp = (struct compreq *)opt->data;
+ compproto = (ntohs(pcomp->proto) << 16) + (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) {
+ if (pcomp->slots <= MAX_VJ_STATES
+ && 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 = pcomp->slots < MIN_VJ_STATES ?
+ MIN_VJ_STATES : MAX_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) {
+ if (pcomp->slots > MAX_VJ_STATES)
+ pcomp->slots = MAX_VJ_STATES;
+ else 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..4ef00e4
--- /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 || 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 < 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..543a75d
--- /dev/null
+++ b/usr.sbin/ppp/ipv6cp.c
@@ -0,0 +1,782 @@
+/*-
+ * 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, const struct iface_addr *addr)
+{
+}
+
+void
+ipv6cp_IfaceAddrDeleted(struct ipv6cp *ipv6cp, const struct iface_addr *addr)
+{
+}
+
+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_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)
+{
+ /* 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(int 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 >= 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..bfc7376
--- /dev/null
+++ b/usr.sbin/ppp/lcp.c
@@ -0,0 +1,1292 @@
+/*-
+ * 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(int 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 < 0 || 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, " 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.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)) {
+ int sz = strlen(lcp->want_callback.msg);
+
+ if (sz > sizeof o->data - 1) {
+ sz = sizeof o->data - 1;
+ log_Printf(LogWARN, "Truncating E164 data to %d 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)
+{
+ /* 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 sz, pos, op, callback_req, chap_type;
+ 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 = op = callback_req = 0;
+
+ while (end - cp >= 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);
+ 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:
+ req = (struct lqrreq *)opt;
+ 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;
+ 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, sz,
+ opt->data + 1);
+ break;
+ case CALLBACK_LOCATION:
+ log_Printf(LogLCP, "%s Location %.*s\n", request, sz, opt->data + 1);
+ break;
+ case CALLBACK_E164:
+ log_Printf(LogLCP, "%s E.164 (%.*s)\n", request, sz, opt->data + 1);
+ break;
+ case CALLBACK_NAME:
+ log_Printf(LogLCP, "%s Name %.*s\n", request, 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 %d 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 - 3 < sizeof p->dl->peer.enddisc.address &&
+ 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 > opt->hdr.len - 2)
+ 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, 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..dca86b7
--- /dev/null
+++ b/usr.sbin/ppp/lcp.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$
+ */
+
+/* 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 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..d5aaca3
--- /dev/null
+++ b/usr.sbin/ppp/link.c
@@ -0,0 +1,383 @@
+/*-
+ * 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)
+{
+ int 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)
+{
+ int 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;
+}
+
+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 || pri >= LINK_QUEUES(l))
+ pri = 0;
+
+ 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)
+{
+ int 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.SaveInDiscards++;
+ 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..89b6230
--- /dev/null
+++ b/usr.sbin/ppp/link.h
@@ -0,0 +1,80 @@
+/*-
+ * 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 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..b2a6b99
--- /dev/null
+++ b/usr.sbin/ppp/log.c
@@ -0,0 +1,520 @@
+/*-
+ * 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",
+ "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..f3fef65
--- /dev/null
+++ b/usr.sbin/ppp/log.h
@@ -0,0 +1,104 @@
+/*-
+ * 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 LogSYNC (18) /* syslog(LOG_INFO, ....) */
+#define LogTCPIP (19)
+#define LogTIMER (20) /* syslog(LOG_DEBUG, ....) */
+#define LogTUN (21) /* If set, tun%d is output with each message */
+#define LogWARN (22) /* Sent to VarTerm else syslog(LOG_WARNING, ) */
+#define LogERROR (23) /* syslog(LOG_ERR, ....), + sent to VarTerm */
+#define LogALERT (24) /* syslog(LOG_ALERT, ....) */
+
+#define LogMAXCONF (21)
+#define LogMAX (24)
+
+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..f72c899
--- /dev/null
+++ b/usr.sbin/ppp/lqr.c
@@ -0,0 +1,447 @@
+/*-
+ * 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 %d, 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;
+ int 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;
+ 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 ECHO LQR packets lost **\n",
+ lcp->fsm.link->name);
+ log_Printf(LogLQM, "%s: Too many ECHO LQR 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, 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;
+ }
+
+ p->hdlc.lqm.lqr.SaveInLQRs++;
+
+ 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;
+ u_int32_t lastLQR;
+
+ 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 {
+ /*
+ * Remember our PeerInLQRs, then convert byte order and save
+ */
+ lastLQR = p->hdlc.lqm.lqr.peer.PeerInLQRs;
+
+ lqr_ChangeOrder(lqr, &p->hdlc.lqm.lqr.peer);
+ lqr_Dump(l->name, "Input", &p->hdlc.lqm.lqr.peer);
+ /* we have received an LQR from peer */
+ p->hdlc.lqm.lqr.resent = 0;
+
+ /*
+ * Generate an LQR response if we're not running an LQR timer OR
+ * two successive LQR's PeerInLQRs are the same OR we're not going to
+ * send our next one before the peers max timeout.
+ */
+ if (p->hdlc.lqm.timer.load == 0 ||
+ !(p->hdlc.lqm.method & LQM_LQR) ||
+ (lastLQR && lastLQR == p->hdlc.lqm.lqr.peer.PeerInLQRs) ||
+ (p->hdlc.lqm.lqr.peer_timeout &&
+ p->hdlc.lqm.timer.rest * 100 / SECTICKS >
+ p->hdlc.lqm.lqr.peer_timeout))
+ 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);
+
+ 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 = LQM_ECHO;
+ 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);
+
+ if (lcp->want_lqrperiod) {
+ log_Printf(LogLQM, "%s: Will send %s every %d.%02d secs\n",
+ physical->link.name,
+ physical->hdlc.lqm.method & LQM_LQR ? "LQR" : "ECHO LQR",
+ lcp->want_lqrperiod / 100, lcp->want_lqrperiod % 100);
+ physical->hdlc.lqm.timer.load = lcp->want_lqrperiod * SECTICKS / 100;
+ physical->hdlc.lqm.timer.func = SendLqrReport;
+ physical->hdlc.lqm.timer.name = "lqm";
+ physical->hdlc.lqm.timer.arg = lcp;
+ } else {
+ physical->hdlc.lqm.timer.load = 0;
+ if (!lcp->his_lqrperiod)
+ log_Printf(LogLQM, "%s: LQR/ECHO LQR 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);
+ }
+}
+
+static struct mbuf *
+lqr_LayerPush(struct bundle *b, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ int len;
+
+ if (!p) {
+ /* Oops - can't happen :-] */
+ m_freem(bp);
+ return NULL;
+ }
+
+ /*
+ * 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 before 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 proto and acf layers.
+ * If we were after these, it makes alignment more of a pain, and we
+ * don't do LQR without these layers.
+ */
+
+ bp = m_pullup(bp);
+ len = m_length(bp);
+
+ if (!physical_IsSync(p))
+ p->hdlc.lqm.OutOctets += hdlc_WrapperOctets(&l->lcp, *proto);
+ p->hdlc.lqm.OutOctets += acf_WrapperOctets(&l->lcp, *proto) +
+ proto_WrapperOctets(&l->lcp, *proto) + len + 1;
+ p->hdlc.lqm.OutPackets++;
+
+ if (*proto == PROTO_LQR) {
+ /* Overwrite the entire packet (created in SendLqrData()) */
+ struct lqrdata 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.SaveInLQRs;
+ lqr.PeerInPackets = p->hdlc.lqm.SaveInPackets;
+ lqr.PeerInDiscards = p->hdlc.lqm.SaveInDiscards;
+ lqr.PeerInErrors = p->hdlc.lqm.SaveInErrors;
+ lqr.PeerInOctets = p->hdlc.lqm.SaveInOctets;
+ lqr.PeerOutPackets = p->hdlc.lqm.OutPackets;
+ lqr.PeerOutOctets = p->hdlc.lqm.OutOctets;
+ if (p->hdlc.lqm.lqr.peer.LastOutLQRs == p->hdlc.lqm.lqr.OutLQRs) {
+ /*
+ * only increment if it's the first time or we've got a reply
+ * from the last one
+ */
+ lqr.PeerOutLQRs = ++p->hdlc.lqm.lqr.OutLQRs;
+ lqr_Dump(l->name, "Output", &lqr);
+ } else {
+ lqr.PeerOutLQRs = p->hdlc.lqm.lqr.OutLQRs;
+ lqr_Dump(l->name, "Output (again)", &lqr);
+ }
+ lqr_ChangeOrder(&lqr, (struct lqrdata *)MBUF_CTOP(bp));
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+lqr_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp, u_short *proto)
+{
+ /*
+ * We mark the packet as ours but don't do anything 'till it's dispatched
+ * to lqr_Input()
+ */
+ 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..16bab39
--- /dev/null
+++ b/usr.sbin/ppp/lqr.h
@@ -0,0 +1,71 @@
+/*-
+ * 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) */
+};
+
+/*
+ * 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 link;
+struct bundle;
+
+extern void lqr_Dump(const char *, const char *, 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..7517663
--- /dev/null
+++ b/usr.sbin/ppp/main.c
@@ -0,0 +1,675 @@
+/*-
+ * 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;
+
+void
+Cleanup(int excode)
+{
+ 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(EX_TERM);
+}
+
+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)
+{
+ signal(SIGCONT, SIG_DFL);
+ prompt_Continue(SignalPrompt);
+}
+
+static void
+TerminalStop(int signo)
+{
+ prompt_Suspend(SignalPrompt);
+ signal(SIGCONT, TerminalCont);
+ raise(SIGSTOP);
+}
+
+static void
+BringDownServer(int signo)
+{
+ /* Drops all child prompts too ! */
+ if (server_Close(SignalBundle))
+ log_Printf(LogPHASE, "Closed server socket\n");
+}
+
+static void
+RestartServer(int signo)
+{
+ /* 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 NOALIAS
+ " [-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, f, holdfd[3], label;
+ 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
+ PacketAliasInit();
+#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)
+ bundle->cfg.opt |= 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..46e63b1
--- /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(int);
+extern void AbortProgram(int);
diff --git a/usr.sbin/ppp/mbuf.c b/usr.sbin/ppp/mbuf.c
new file mode 100644
index 0000000..0edf1b9
--- /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;
+
+int
+m_length(struct mbuf *bp)
+{
+ int 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, size_t 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 (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 (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)
+{
+ int 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..82a7428
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.h
@@ -0,0 +1,118 @@
+/*-
+ * 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) */
+ 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 */
+ /* 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 : NULL)
+
+#define CONST_MBUF_CTOP(bp) \
+ ((bp) ? (const u_char *)((bp)+1) + (bp)->m_offset : NULL)
+
+#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 int 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, size_t);
+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..3fd9b95
--- /dev/null
+++ b/usr.sbin/ppp/mp.c
@@ -0,0 +1,1213 @@
+/*-
+ * 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, struct fsm *fp)
+{
+ /* The given FSM (ccp) is about to start up ! */
+}
+
+static void
+mp_LayerUp(void *v, struct fsm *fp)
+{
+ /* The given fsm (ccp) is now up */
+
+ bundle_CalculateBandwidth(fp->bundle); /* Against ccp_MTUOverhead */
+}
+
+static void
+mp_LayerDown(void *v, struct fsm *fp)
+{
+ /* The given FSM (ccp) has been told to come down */
+}
+
+static void
+mp_LayerFinish(void *v, 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;
+ 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 == -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;
+ u_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 %ld-%lu, length %d\n",
+ 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 %d 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, int len)
+{
+ static char result[100]; /* Used immediately after it's returned */
+ int f, header;
+
+ switch (c) {
+ case ENDDISC_NULL:
+ sprintf(result, "Null Class");
+ break;
+
+ case ENDDISC_LOCAL:
+ snprintf(result, sizeof result, "Local Addr: %.*s", 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[%d] ???", 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[%d] ???", len);
+ break;
+
+ case ENDDISC_MAGIC:
+ sprintf(result, "Magic: 0x");
+ header = strlen(result);
+ if (len > sizeof result - header - 1)
+ 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", len, address);
+ break;
+
+ default:
+ sprintf(result, "%d: ", (int)c);
+ header = strlen(result);
+ if (len > sizeof result - header - 1)
+ 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;
+ int s;
+
+ 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;
+
+ s = ID0socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "set enddisc: socket(): %s\n", strerror(errno));
+ return 2;
+ }
+ if (arp_EtherAddr(s, 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));
+ close(s);
+ return 4;
+ }
+ close(s);
+ } 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)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+
+ bundle_ReceiveDatalink(bundle, s->fd);
+}
+
+static int
+mpserver_Write(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ /* 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 && 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..525ee29
--- /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 *, int);
+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..606ad0f
--- /dev/null
+++ b/usr.sbin/ppp/mppe.c
@@ -0,0 +1,814 @@
+/*-
+ * 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;
+ int 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, int pri, 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)
+{
+ 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, struct ccp *ccp, u_short proto, struct mbuf *mi)
+{
+}
+
+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, 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");
+ ua_htonl(0x0, 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, 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, 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..8172fd6
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.c
@@ -0,0 +1,594 @@
+/*-
+ * 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 *);
+
+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 = PacketAliasRedirectPort(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 = PacketAliasRedirectAddr(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, 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 = PacketAliasRedirectProto(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 PacketAliasProxyRule(cmd);
+}
+
+int
+nat_SetTarget(struct cmdargs const *arg)
+{
+ struct in_addr addr;
+
+ if (arg->argc == arg->argn) {
+ addr.s_addr = INADDR_ANY;
+ PacketAliasSetTarget(addr);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
+ addr.s_addr = INADDR_ANY;
+ PacketAliasSetTarget(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;
+ }
+
+ PacketAliasSetTarget(addr);
+ return 0;
+}
+
+#ifndef NO_FW_PUNCH
+int
+nat_PunchFW(struct cmdargs const *arg)
+{
+ char *end;
+ long base, count;
+
+ if (arg->argc == arg->argn) {
+ PacketAliasSetMode(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;
+
+ PacketAliasSetFWBase(base, count);
+ PacketAliasSetMode(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) {
+ PacketAliasSetSkinnyPort(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;
+
+ PacketAliasSetSkinnyPort(port);
+
+ return 0;
+}
+
+static struct mbuf *
+nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ int pri, 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));
+ PacketAliasOut(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, 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 = PacketAliasIn(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 */
+ fptr = malloc(bp->m_len);
+ bp = mbuf_Read(bp, fptr, bp->m_len);
+ PacketAliasSaveFragment(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 = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
+ nfrags++;
+ PacketAliasFragmentIn(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 (PacketAliasSetMode(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..e1acafb
--- /dev/null
+++ b/usr.sbin/ppp/ncp.c
@@ -0,0 +1,537 @@
+/*-
+ * 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 */
+};
+
+static u_short default_urgent_udp_ports[] = { };
+
+#define NDEFTCPPORTS \
+ (sizeof default_urgent_tcp_ports / sizeof default_urgent_tcp_ports[0])
+#define NDEFUDPPORTS \
+ (sizeof default_urgent_udp_ports / sizeof default_urgent_udp_ports[0])
+
+void
+ncp_Init(struct ncp *ncp, struct bundle *bundle)
+{
+ ncp->afq = AF_INET;
+ ncp->route = NULL;
+
+ ncp->cfg.urgent.tcp.nports = ncp->cfg.urgent.tcp.maxports = NDEFTCPPORTS;
+ ncp->cfg.urgent.tcp.port = (u_short *)malloc(NDEFTCPPORTS * sizeof(u_short));
+ 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 = NDEFUDPPORTS;
+ ncp->cfg.urgent.udp.port = (u_short *)malloc(NDEFUDPPORTS * sizeof(u_short));
+ memcpy(ncp->cfg.urgent.udp.port, default_urgent_udp_ports,
+ NDEFUDPPORTS * sizeof(u_short));
+
+
+ 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, struct bundle *bundle)
+{
+ 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, int 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 < 0 || pri >= IPCP_QUEUES(ipcp)) {
+ log_Printf(LogERROR, "Can't store in ip queue %d\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 < 0 || pri >= IPV6CP_QUEUES(ipcp)) {
+ log_Printf(LogERROR, "Can't store in ipv6 queue %d\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, int *af, 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)
+{
+ int 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;
+ int 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)
+{
+ int p;
+
+ for (p = 0; p < range->nports; p++)
+ if (range->port[p] == port) {
+ if (p != range->nports - 1)
+ 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;
+ int 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..c9c3be5
--- /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, int, 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..112cfbc
--- /dev/null
+++ b/usr.sbin/ppp/ncpaddr.c
@@ -0,0 +1,1013 @@
+/*-
+ * 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;
+}
+
+static void
+adjust_linklocal(struct sockaddr_in6 *sin6)
+{
+ /* XXX: ?????!?!?!!!!! This is horrible ! */
+#if 0
+ /*
+ * 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;
+ adjust_linklocal(&sin6);
+#ifdef NI_WITHSCOPEID
+ if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
+ NULL, 0, NI_WITHSCOPEID | NI_NUMERICHOST) != 0)
+#else
+ if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
+ NULL, 0, NI_NUMERICHOST) != 0)
+#endif
+ 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..15ace22
--- /dev/null
+++ b/usr.sbin/ppp/netgraph.c
@@ -0,0 +1,741 @@
+/*-
+ * 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:"
+
+int
+ng_DeviceSize(void)
+{
+ return sizeof(struct ngdevice);
+}
+
+static int
+ng_MessageOut(struct ngdevice *dev, struct physical *p, const char *data)
+{
+ char path[NG_PATHSIZ];
+ int len, pos, dpos;
+ char *fmt;
+
+ /*
+ * 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 < 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];
+ int 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, p, v) ? n : -1;
+ }
+ return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : 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, 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, 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)
+{
+ int 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, 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..ffa012a
--- /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 int ng_DeviceSize(void);
diff --git a/usr.sbin/ppp/pap.c b/usr.sbin/ppp/pap.c
new file mode 100644
index 0000000..8a1d112
--- /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, p))
+ 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..bff1198
--- /dev/null
+++ b/usr.sbin/ppp/physical.c
@@ -0,0 +1,1131 @@
+/*
+ * 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>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/tty.h> /* TIOCOUTQ */
+#include <sys/uio.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 "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "tcp.h"
+#include "udp.h"
+#include "exec.h"
+#include "tty.h"
+#ifndef NOI4B
+#include "i4b.h"
+#endif
+#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 int
+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 *);
+ int (*DeviceSize)(void);
+} devices[] = {
+#ifndef NOI4B
+ /*
+ * This must come before ``tty'' so that the probe routine is
+ * able to identify it as a more specific type of terminal device.
+ */
+ { i4b_Create, i4b_iov2device, i4b_DeviceSize },
+#endif
+ { 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, 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;
+}
+
+int
+physical_GetSpeed(struct physical *p)
+{
+ if (p->handler && p->handler->speed)
+ return (*p->handler->speed)(p);
+
+ return 0;
+}
+
+int
+physical_SetSpeed(struct physical *p, int speed)
+{
+ if (IntToSpeed(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,
+ const fd_set *fdset)
+{
+ 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 (%d): %s\n", p->link.name,
+ p->fd, 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)
+{
+ 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, h, type;
+
+ 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;
+}
+
+int
+physical_MaxDeviceSize()
+{
+ int biggest, sz, n;
+
+ biggest = sizeof(struct device);
+ for (sz = 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 {
+ iov[*niov].iov_base = malloc(sz);
+ 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)
+{
+ int f, pos;
+
+ 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, struct bundle *bundle)
+{
+ int devno, h, wasfd, err;
+ char *dev;
+
+ 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;
+}
diff --git a/usr.sbin/ppp/physical.h b/usr.sbin/ppp/physical.h
new file mode 100644
index 0000000..f1e3ec5
--- /dev/null
+++ b/usr.sbin/ppp/physical.h
@@ -0,0 +1,173 @@
+/*
+ * 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 *);
+ int (*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 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 - (int)(&((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 *, struct bundle *);
+extern int physical_Raw(struct physical *);
+extern int physical_GetSpeed(struct physical *);
+extern int physical_SetSpeed(struct physical *, int);
+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 int 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 *);
diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4
new file mode 100644
index 0000000..cac056f
--- /dev/null
+++ b/usr.sbin/ppp/ppp.8.m4
@@ -0,0 +1,5982 @@
+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 September 20, 1995
+.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's 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 isn't 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's 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 ISDN.
+If
+.Nm
+is given a raw B-channel i4b device to open as a link, it's able to talk
+to the
+.Xr isdnd 8
+daemon to establish an ISDN connection.
+.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're 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
+Your kernel must {include} a tunnel device (the GENERIC kernel includes
+one by default).
+If it doesn't, or if you require more than one tun
+interface, you'll need to rebuild your kernel with the following line in
+your kernel configuration file:
+.Pp
+.Dl pseudo-device tun N
+.Pp
+where
+.Ar N
+is the maximum number of
+.Em PPP
+connections you wish to support.
+.It
+Check your
+.Pa /dev
+directory for the tunnel device entries
+.Pa /dev/tunN ,
+where
+.Sq N
+represents the number of the tun device, starting at zero.
+If they don't exist, you can create them by running "sh ./MAKEDEV tunN".
+This will create tun devices 0 through
+.Ar N .
+.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 .
+Don't 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/cuaa0
+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 can't 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'll 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's 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/cuaa0
+ 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's 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
+couldn't 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.
+.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's 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
+Don't 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/cuaa1
+ppp ON awfulhak> set speed 38400
+ppp ON awfulhak> term
+deflink: Entering terminal mode on /dev/cuaa1
+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
+doesn't 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
+Don't forget to send a
+.Dv HUP
+signal to
+.Xr inetd 8
+after you've 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'll 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're enabling PAP, you'll also need this in the
+.Pa /etc/ppp/ppp.conf
+profile:
+.Bd -literal -offset indent
+ set authname MyAuthName
+ set authkey MyAuthKey
+.Ed
+.Pp
+We're 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've 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's 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's 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 won't 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'd like to use 192.244.177.38 as my address if it is possible, but I'll
+also accept any IP address between 192.244.177.0 and 192.244.177.255.
+.It
+I'd like to make him use 192.244.177.2 as his own address, but I'll 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'll accept/permit any IP address but I'll
+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's 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's 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're 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/cuaa0
+set speed 115200
+.Ed
+.Pp
+Cuaa0 is the first serial port on
+.Fx .
+If you're 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 don't 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 don't 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 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 couldn't 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/cuaa0 /dev/cuaa1 /dev/cuaa2
+ 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/cuaa0
+ link 2 set device /dev/cuaa1
+ link 3 set device /dev/cuaa2
+.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 doesn't agree,
+.Nm
+will send ECHO LQR requests instead.
+These packets pass no information of interest, but they
+.Em MUST
+be replied to by the peer.
+.Pp
+Whether using LQR or ECHO LQR,
+.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.
+.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 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's 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 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's 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 doesn't 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
+This is replaced with the date on which
+.Nm
+was compiled.
+.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's 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've 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's not considered polite to use this command on
+a Finite State Machine that's 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 isn't 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's 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 cuaa0
+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's 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's 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's not set,
+.Nm
+assumes that this is because the device doesn't support carrier (which
+is true for most
+.Dq laplink
+NULL-modem cables), logs the fact and stops checking
+for carrier.
+.Pp
+As ptys don't support the TIOCMGET ioctl, the tty device will switch all
+carrier detection off when it detects that the device is a pty.
+.It ISDN (i4b) Devices
+Carrier is checked once per second for 6 seconds.
+If it's not set after
+the sixth second, the connection attempt is considered to have failed and
+the device is closed.
+Carrier is always required for i4b devices.
+.It PPPoE (netgraph) Devices
+Carrier is checked once per second for 5 seconds.
+If it's 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 don't 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 isn't increased, this will result in
+.Nm Ns No 's
+inability to detect when the link is dropped, as
+.Nm
+assumes that the device isn't 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
+isn't 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 ISDN and serial device names are expected to begin with
+.Pa /dev/ .
+ISDN devices are usually called
+.Pa i4brbchX
+and 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 doesn't 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's 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 isn't 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 Ar frequency
+This command sets the
+.Ar frequency
+in seconds at which
+.Em LQR
+or
+.Em ECHO LQR
+packets are sent.
+The default is 30 seconds.
+You must also use the
+.Dq enable lqr
+command if you wish to send LQR 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's 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's
+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.
+.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 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's 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 doesn't 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 don't {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 don't {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's 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's 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 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 doesn't 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
+doesn't 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's 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 isdn 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..2a81381
--- /dev/null
+++ b/usr.sbin/ppp/pred.c
@@ -0,0 +1,343 @@
+/*-
+ * 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, struct fsm_opt *o)
+{
+ 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, struct fsm_opt *o)
+{
+ 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, int pri, 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, struct ccp *ccp, u_short proto, struct mbuf *bp)
+{
+}
+
+static const char *
+Pred1DispOpts(struct fsm_opt *o)
+{
+ return NULL;
+}
+
+static void
+Pred1InitOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ o->hdr.len = 2;
+}
+
+static int
+Pred1SetOpts(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ 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..839e655
--- /dev/null
+++ b/usr.sbin/ppp/prompt.c
@@ -0,0 +1,571 @@
+/*-
+ * 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, 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)
+{
+ 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(EX_NORMAL);
+ 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, struct bundle *bundle, const fd_set *fdset)
+{
+ /* 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 */
+ int 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..e471998
--- /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, struct link *l, struct mbuf *bp,
+ int pri, 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, 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..06f716b
--- /dev/null
+++ b/usr.sbin/ppp/radius.c
@@ -0,0 +1,1208 @@
+/*
+ * 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/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"
+
+#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;
+ }
+
+ *buf = malloc(*len);
+ 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(LogPHASE, "Radius(%s): ACCEPT received\n", stype);
+ if (!r->cx.auth) {
+ rad_close(r->cx.rad);
+ return;
+ }
+ break;
+
+ case RAD_ACCESS_REJECT:
+ log_Printf(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(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:
+ log_Printf(LogPHASE, "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(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(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(LogPHASE, " Filter \"%s\"\n", r->filterid);
+ break;
+
+ case RAD_SESSION_TIMEOUT:
+ r->sessiontime = rad_cvt_int(data);
+ log_Printf(LogPHASE, " Session-Timeout %lu\n", r->sessiontime);
+ break;
+
+ case RAD_FRAMED_IP_NETMASK:
+ r->mask = rad_cvt_addr(data);
+ log_Printf(LogPHASE, " Netmask %s\n", inet_ntoa(r->mask));
+ break;
+
+ case RAD_FRAMED_MTU:
+ r->mtu = rad_cvt_int(data);
+ log_Printf(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(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(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(LogPHASE, " Reply-Message \"%s\"\n", r->repstr);
+ break;
+
+#ifndef NOINET6
+ case RAD_FRAMED_IPV6_PREFIX:
+ free(r->ipv6prefix);
+ r->ipv6prefix = rad_cvt_ipv6prefix(data, len);
+ inet_ntop(AF_INET6, &r->ipv6prefix[2], ipv6addr, sizeof(ipv6addr));
+ log_Printf(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(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.
+ */
+ ((const char *)data)++;
+ 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(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.
+ */
+ ((const char *)data)++;
+ 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(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(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(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(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(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(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, const fd_set *fdset)
+{
+ radius_Continue(descriptor2radius(d), 1);
+}
+
+/*
+ * Behave as a struct fdescriptor (descriptor.h)
+ */
+static int
+radius_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, 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, struct bundle *bundle, const fd_set *fdset)
+{
+ /* 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 rad_handle *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, RAD_NAS_PORT_TYPE, type) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad));
+ rad_close(rad);
+ return 0;
+ }
+
+ if ((slot = physical_Slot(p)) >= 0)
+ if (rad_put_int(rad, RAD_NAS_PORT, slot) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad));
+ rad_close(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)
+{
+ struct timeval tv;
+ int got;
+ char hostname[MAXHOSTNAMELEN];
+#if 0
+ struct hostent *hp;
+ struct in_addr hostaddr;
+#endif
+#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;
+ }
+ 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;
+ }
+ 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);
+ 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);
+ 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 0
+ if ((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;
+ }
+ }
+#endif
+ if (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;
+ }
+ }
+
+ radius_put_physical_details(r->cx.rad, authp->physical);
+
+ r->cx.auth = authp;
+ if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
+ radius_Process(r, got);
+ else {
+ log_Printf(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];
+#if 0
+ struct hostent *hp;
+ struct in_addr hostaddr;
+#endif
+
+ 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 (gethostname(hostname, sizeof hostname) != 0)
+ log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
+ else {
+#if 0
+ if ((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;
+ }
+ }
+#endif
+ if (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->cx.rad, 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)
+ /* 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;
+ }
+
+ 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");
+}
diff --git a/usr.sbin/ppp/radius.h b/usr.sbin/ppp/radius.h
new file mode 100644
index 0000000..29b5190
--- /dev/null
+++ b/usr.sbin/ppp/radius.h
@@ -0,0 +1,118 @@
+/*
+ * 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
+
+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 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_Init(struct radius *);
+extern void radius_Destroy(struct radius *);
+
+extern void radius_Show(struct radius *, struct prompt *);
+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
+
+/* 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..168911c
--- /dev/null
+++ b/usr.sbin/ppp/route.c
@@ -0,0 +1,919 @@
+/*-
+ * 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_CLONING, 'C' },
+ { RTF_XRESOLVE, 'X' },
+ { RTF_LLINFO, 'L' },
+ { RTF_STATIC, 'S' },
+ { RTF_PROTO1, '1' },
+ { RTF_PROTO2, '2' },
+ { RTF_BLACKHOLE, 'B' },
+#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, int 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 < max; p++)
+ if (p->b_mask & f)
+ *flags++ = p->b_val;
+ *flags = '\0';
+ prompt_Printf(prompt, "%-*.*s", max, 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);
+ 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 %d\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)
+ r = (struct sticky_route *)malloc(sizeof(struct sticky_route));
+ 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);
+ }
+
+ 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..03f7b86
--- /dev/null
+++ b/usr.sbin/ppp/server.c
@@ -0,0 +1,413 @@
+/*-
+ * 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, struct bundle *bundle, const fd_set *fdset)
+{
+ /* 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
+};
+
+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;
+ }
+
+ 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)
+{
+ 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..bebd599
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.c
@@ -0,0 +1,589 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#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 (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..223f659
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.h
@@ -0,0 +1,149 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#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..9fd782a
--- /dev/null
+++ b/usr.sbin/ppp/sync.c
@@ -0,0 +1,80 @@
+/*-
+ * 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, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ log_DumpBp(LogSYNC, "Write", bp);
+ m_settype(bp, MB_SYNCOUT);
+ return bp;
+}
+
+static struct mbuf *
+sync_LayerPull(struct bundle *b, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ struct physical *p = link2physical(l);
+
+ 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 */
+ p->hdlc.lqm.SaveInOctets += m_length(bp) + 1;
+ p->hdlc.lqm.SaveInPackets++;
+ 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..24f58a5
--- /dev/null
+++ b/usr.sbin/ppp/systems.c
@@ -0,0 +1,482 @@
+/*-
+ * 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 len, instring;
+ 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 < 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 ? 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..65679f0
--- /dev/null
+++ b/usr.sbin/ppp/tcp.c
@@ -0,0 +1,211 @@
+/*-
+ * 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, int *auxfd, int *nauxfd)
+{
+ 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..1fdbf02
--- /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))
+
+
+/*-
+ * 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, ssize_t pktlen, u_int16_t maxmss)
+{
+ int 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;
+ int 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 - hlen >= sizeof(struct tcphdr))
+ 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, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ return tcpmss_Check(bundle, bp);
+}
+
+static struct mbuf *
+tcpmss_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ 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..b72ec79
--- /dev/null
+++ b/usr.sbin/ppp/tty.c
@@ -0,0 +1,756 @@
+/*-
+ * 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 {
+ int 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)
+
+int
+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, 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 : 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 int
+tty_Speed(struct physical *p)
+{
+ struct termios ios;
+
+ if (tcgetattr(p->fd, &ios) == -1)
+ return 0;
+
+ return SpeedToInt(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, int *auxfd, int *nauxfd)
+{
+ 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, int *auxfd, int *nauxfd)
+{
+ 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, IntToSpeed(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..f3f0d53
--- /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 int 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..3a5fe06
--- /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)
+
+int
+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, int *auxfd, int *nauxfd)
+{
+ 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, int *auxfd, int *nauxfd)
+{
+ 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..46b8fe6
--- /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 int udp_DeviceSize(void);
diff --git a/usr.sbin/ppp/vjcomp.c b/usr.sbin/ppp/vjcomp.c
new file mode 100644
index 0000000..8bc03db
--- /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, struct mbuf *bp, int pri,
+ 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, 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..c512591
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.8
@@ -0,0 +1,213 @@
+.\" $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 don't 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
+Don't 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 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 aren't 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 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 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..1ff63b3
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.c
@@ -0,0 +1,676 @@
+/*-
+ * 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;
+
+ 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 && errno != EINTR) {
+ 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) {
+ if (len)
+ write(STDOUT_FILENO, Buffer, len);
+ 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..b6b52af
--- /dev/null
+++ b/usr.sbin/pppd/Makefile
@@ -0,0 +1,50 @@
+# $FreeBSD$
+
+# 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 !defined(NOCRYPT) && !defined(NO_OPENSSL) && !defined(RELEASE_CRUNCH)
+DISTRIBUTION=crypto
+CFLAGS+= -DCHAPMS
+SRCS+= chap_ms.c
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+.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..c982461
--- /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 __P((const char *, const char *));
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase __P((int));
+static void check_idle __P((void *));
+static void connect_time_expired __P((void *));
+static int plogin __P((char *, char *, char **, int *));
+static void plogout __P((void));
+static int null_login __P((int));
+static int get_pap_passwd __P((char *));
+static int have_pap_secret __P((void));
+static int have_chap_secret __P((char *, char *, u_int32_t));
+static int ip_addr_check __P((u_int32_t, struct wordlist *));
+static int scan_authfile __P((FILE *, char *, char *, u_int32_t, char *,
+ struct wordlist **, char *));
+static void free_wordlist __P((struct wordlist *));
+static void auth_set_ip_addr __P((int));
+static void auth_script __P((char *));
+static void set_allowed_addrs __P((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[8];
+
+ 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..fb265e6
--- /dev/null
+++ b/usr.sbin/pppd/cbcp.c
@@ -0,0 +1,430 @@
+/*
+ * 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 __P((int unit));
+static void cbcp_open __P((int unit));
+static void cbcp_lowerup __P((int unit));
+static void cbcp_input __P((int unit, u_char *pkt, int len));
+static void cbcp_protrej __P((int unit));
+static int cbcp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((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 __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_resp __P((cbcp_state *us));
+static void cbcp_up __P((cbcp_state *us));
+static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len));
+
+/* init state */
+static void
+cbcp_init(iface)
+ 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(iface)
+ 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(unit)
+ int unit;
+{
+ syslog(LOG_DEBUG, "cbcp_open");
+}
+
+/* process an incomming packet */
+static void
+cbcp_input(unit, inpacket, pktlen)
+ 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 0
+ if (len > pktlen) {
+ syslog(LOG_ERR, "CBCP packet: invalid length");
+ return;
+ }
+#endif
+
+ 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(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((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(us, pckt, pcktlen)
+ 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) {
+ syslog(LOG_DEBUG, "length: %d", len);
+
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ 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;
+ }
+ len -= opt_len;
+ }
+
+ cbcp_resp(us);
+}
+
+static void
+cbcp_resp(us)
+ 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 + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(0, bufp);
+ 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 = 3;
+ PUTCHAR(len , bufp);
+ PUTCHAR(0, bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ (*ipcp_protent.open)(us->us_unit);
+ return;
+ }
+}
+
+static void
+cbcp_send(us, code, buf, len)
+ 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(us, pckt, len)
+ cbcp_state *us;
+ char *pckt;
+ int len;
+{
+ u_char type, delay, addr_type;
+ int opt_len;
+ char address[256];
+
+ if (len) {
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ 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(us)
+ 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..fb21cfd
--- /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 __P((int unit));
+static void ccp_open __P((int unit));
+static void ccp_close __P((int unit, char *));
+static void ccp_lowerup __P((int unit));
+static void ccp_lowerdown __P((int));
+static void ccp_input __P((int unit, u_char *pkt, int len));
+static void ccp_protrej __P((int unit));
+static int ccp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+static void ccp_datainput __P((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 __P((fsm *));
+static int ccp_cilen __P((fsm *));
+static void ccp_addci __P((fsm *, u_char *, int *));
+static int ccp_ackci __P((fsm *, u_char *, int));
+static int ccp_nakci __P((fsm *, u_char *, int));
+static int ccp_rejci __P((fsm *, u_char *, int));
+static int ccp_reqci __P((fsm *, u_char *, int *, int));
+static void ccp_up __P((fsm *));
+static void ccp_down __P((fsm *));
+static int ccp_extcode __P((fsm *, int, int, u_char *, int));
+static void ccp_rack_timeout __P((void *));
+static char *method_name __P((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) __P((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..c3acafb
--- /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 __P((int));
+static void ChapLowerUp __P((int));
+static void ChapLowerDown __P((int));
+static void ChapInput __P((int, u_char *, int));
+static void ChapProtocolReject __P((int));
+static int ChapPrintPkt __P((u_char *, int,
+ void (*) __P((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 __P((void *));
+static void ChapResponseTimeout __P((void *));
+static void ChapReceiveChallenge __P((chap_state *, u_char *, int, int));
+static void ChapRechallenge __P((void *));
+static void ChapReceiveResponse __P((chap_state *, u_char *, int, int));
+static void ChapReceiveSuccess __P((chap_state *, u_char *, int, int));
+static void ChapReceiveFailure __P((chap_state *, u_char *, int, int));
+static void ChapSendStatus __P((chap_state *, int));
+static void ChapSendChallenge __P((chap_state *));
+static void ChapSendResponse __P((chap_state *));
+static void ChapGenChallenge __P((chap_state *));
+
+extern double drand48 __P((void));
+extern void srand48 __P((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) __P((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..4a4f383
--- /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 __P((int, char *, int));
+void ChapAuthPeer __P((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..c3e1f6e
--- /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 __P((u_char *, u_char *, u_char *));
+static void DesEncrypt __P((u_char *, u_char *, u_char *));
+static void MakeKey __P((u_char *, u_char *));
+static u_char Get7Bits __P((u_char *, int));
+static void ChapMS_NT __P((char *, int, char *, int, MS_ChapResponse *));
+#ifdef MSLANMAN
+static void ChapMS_LANMan __P((char *, int, char *, int, MS_ChapResponse *));
+#endif
+
+#ifdef USE_CRYPT
+static void Expand __P((u_char *, u_char *));
+static void Collapse __P((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..7c00883
--- /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 __P((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..59c0281
--- /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 __P((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/fsm.c b/usr.sbin/pppd/fsm.c
new file mode 100644
index 0000000..0c49c70
--- /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 __P((void *));
+static void fsm_rconfreq __P((fsm *, int, u_char *, int));
+static void fsm_rconfack __P((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __P((fsm *, int, u_char *, int));
+static void fsm_rtermack __P((fsm *));
+static void fsm_rcoderej __P((fsm *, u_char *, int));
+static void fsm_sconfreq __P((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) __P((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..7b43b7a
--- /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 */
+ __P((fsm *));
+ int (*cilen) /* Length of our Configuration Information */
+ __P((fsm *));
+ void (*addci) /* Add our Configuration Information */
+ __P((fsm *, u_char *, int *));
+ int (*ackci) /* ACK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*nakci) /* NAK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*rejci) /* Reject our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*reqci) /* Request peer's Configuration Information */
+ __P((fsm *, u_char *, int *, int));
+ void (*up) /* Called when fsm reaches OPENED state */
+ __P((fsm *));
+ void (*down) /* Called when fsm leaves OPENED state */
+ __P((fsm *));
+ void (*starting) /* Called when we want the lower layer */
+ __P((fsm *));
+ void (*finished) /* Called when we don't want the lower layer */
+ __P((fsm *));
+ void (*protreject) /* Called when Protocol-Reject received */
+ __P((int));
+ void (*retransmit) /* Retransmission is necessary */
+ __P((fsm *));
+ int (*extcode) /* Called when unknown code received */
+ __P((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 __P((fsm *));
+void fsm_lowerup __P((fsm *));
+void fsm_lowerdown __P((fsm *));
+void fsm_open __P((fsm *));
+void fsm_close __P((fsm *, char *));
+void fsm_input __P((fsm *, u_char *, int));
+void fsm_protreject __P((fsm *));
+void fsm_sdata __P((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..576b200
--- /dev/null
+++ b/usr.sbin/pppd/ipcp.c
@@ -0,0 +1,1530 @@
+/*
+ * 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 <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 __P((fsm *)); /* Reset our CI */
+static int ipcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipcp_up __P((fsm *)); /* We're UP */
+static void ipcp_down __P((fsm *)); /* We're DOWN */
+static void ipcp_script __P((fsm *, char *)); /* Run an up/down script */
+static void ipcp_finished __P((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 __P((int));
+static void ipcp_open __P((int));
+static void ipcp_close __P((int, char *));
+static void ipcp_lowerup __P((int));
+static void ipcp_lowerdown __P((int));
+static void ipcp_input __P((int, u_char *, int));
+static void ipcp_protrej __P((int));
+static int ipcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+static void ip_check_options __P((void));
+static int ip_demand_conf __P((int));
+static int ip_active_pkt __P((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 __P((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) __P((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..2bc795d
--- /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 __P((u_int32_t));
+
+extern struct protent ipcp_protent;
diff --git a/usr.sbin/pppd/ipxcp.c b/usr.sbin/pppd/ipxcp.c
new file mode 100644
index 0000000..bdf24d1
--- /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 __P((fsm *)); /* Reset our CI */
+static int ipxcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipxcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipxcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipxcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipxcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipxcp_up __P((fsm *)); /* We're UP */
+static void ipxcp_down __P((fsm *)); /* We're DOWN */
+static void ipxcp_script __P((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 __P((int));
+static void ipxcp_open __P((int));
+static void ipxcp_close __P((int, char *));
+static void ipxcp_lowerup __P((int));
+static void ipxcp_lowerdown __P((int));
+static void ipxcp_input __P((int, u_char *, int));
+static void ipxcp_protrej __P((int));
+static int ipxcp_printpkt __P((u_char *, int,
+ void (*) __P((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) __P((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..7400987
--- /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 __P((fsm *)); /* Reset our CI */
+static int lcp_cilen __P((fsm *)); /* Return length of our CI */
+static void lcp_addci __P((fsm *, u_char *, int *)); /* Add our CI to pkt */
+static int lcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int lcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int lcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int lcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv peer CI */
+static void lcp_up __P((fsm *)); /* We're UP */
+static void lcp_down __P((fsm *)); /* We're DOWN */
+static void lcp_starting __P((fsm *)); /* We need lower layer up */
+static void lcp_finished __P((fsm *)); /* We need lower layer down */
+static int lcp_extcode __P((fsm *, int, int, u_char *, int));
+static void lcp_rprotrej __P((fsm *, u_char *, int));
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup __P((int));
+static void lcp_echo_lowerdown __P((int));
+static void LcpEchoTimeout __P((void *));
+static void lcp_received_echo_reply __P((fsm *, int, u_char *, int));
+static void LcpSendEchoRequest __P((fsm *));
+static void LcpLinkFailure __P((fsm *));
+static void LcpEchoCheck __P((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 __P((int));
+static void lcp_input __P((int, u_char *, int));
+static void lcp_protrej __P((int));
+static int lcp_printpkt __P((u_char *, int,
+ void (*) __P((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) __P((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..2a19e26
--- /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 __P((int));
+void lcp_close __P((int, char *));
+void lcp_lowerup __P((int));
+void lcp_lowerdown __P((int));
+void lcp_sprotrej __P((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..1e13d02
--- /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 __P((void));
+extern void srand48 __P((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..82f65cf
--- /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 __P((void)); /* Initialize the magic number generator */
+u_int32_t magic __P((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..c2d0abf
--- /dev/null
+++ b/usr.sbin/pppd/main.c
@@ -0,0 +1,1717 @@
+/*
+ * 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"
+#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 __P((void));
+static void cleanup __P((void));
+static void close_tty __P((void));
+static void get_input __P((void));
+static void calltimeout __P((void));
+static struct timeval *timeleft __P((struct timeval *));
+static void kill_my_pg __P((int));
+static void hup __P((int));
+static void term __P((int));
+static void chld __P((int));
+static void toggle_debug __P((int));
+static void open_ccp __P((int));
+static void bad_signal __P((int));
+static void holdoff_end __P((void *));
+static int device_script __P((char *, int, int));
+static void reap_kids __P((void));
+static void pr_log __P((void *, char *, ...));
+
+extern char *ttyname __P((int));
+extern char *getlogin __P((void));
+int main __P((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,
+ &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) __P((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) __P((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) __P((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) __P((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) __P((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..d1e679b
--- /dev/null
+++ b/usr.sbin/pppd/options.c
@@ -0,0 +1,2570 @@
+/*
+ * 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 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 __P((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 __P((char *, int));
+static int setspeed __P((char *));
+static int setdebug __P((char **));
+static int setkdebug __P((char **));
+static int setpassive __P((char **));
+static int setsilent __P((char **));
+static int noopt __P((char **));
+static int setnovj __P((char **));
+static int setnovjccomp __P((char **));
+static int setvjslots __P((char **));
+static int reqpap __P((char **));
+static int nopap __P((char **));
+#ifdef OLD_OPTIONS
+static int setupapfile __P((char **));
+#endif
+static int nochap __P((char **));
+static int reqchap __P((char **));
+static int noaccomp __P((char **));
+static int noasyncmap __P((char **));
+static int noip __P((char **));
+static int nomagicnumber __P((char **));
+static int setasyncmap __P((char **));
+static int setescape __P((char **));
+static int setmru __P((char **));
+static int setmtu __P((char **));
+#ifdef CBCP_SUPPORT
+static int setcbcp __P((char **));
+#endif
+static int nomru __P((char **));
+static int nopcomp __P((char **));
+static int setconnector __P((char **));
+static int setdisconnector __P((char **));
+static int setwelcomer __P((char **));
+static int setmaxcon __P((char **));
+static int setmaxconnect __P((char **));
+static int setdomain __P((char **));
+static int setnetmask __P((char **));
+static int setcrtscts __P((char **));
+static int setnocrtscts __P((char **));
+static int setxonxoff __P((char **));
+static int setnodetach __P((char **));
+static int setupdetach __P((char **));
+static int setmodem __P((char **));
+static int setlocal __P((char **));
+static int setlock __P((char **));
+static int setname __P((char **));
+static int setuser __P((char **));
+static int setremote __P((char **));
+static int setauth __P((char **));
+static int setnoauth __P((char **));
+static int readfile __P((char **));
+static int callfile __P((char **));
+static int setdefaultroute __P((char **));
+static int setnodefaultroute __P((char **));
+static int setproxyarp __P((char **));
+static int setnoproxyarp __P((char **));
+static int setpersist __P((char **));
+static int setnopersist __P((char **));
+static int setdologin __P((char **));
+static int setusehostname __P((char **));
+static int setnoipdflt __P((char **));
+static int setlcptimeout __P((char **));
+static int setlcpterm __P((char **));
+static int setlcpconf __P((char **));
+static int setlcpfails __P((char **));
+static int setipcptimeout __P((char **));
+static int setipcpterm __P((char **));
+static int setipcpconf __P((char **));
+static int setipcpfails __P((char **));
+static int setpaptimeout __P((char **));
+static int setpapreqs __P((char **));
+static int setpapreqtime __P((char **));
+static int setchaptimeout __P((char **));
+static int setchapchal __P((char **));
+static int setchapintv __P((char **));
+static int setipcpaccl __P((char **));
+static int setipcpaccr __P((char **));
+static int setlcpechointv __P((char **));
+static int setlcpechofails __P((char **));
+static int noccp __P((char **));
+static int setbsdcomp __P((char **));
+static int setnobsdcomp __P((char **));
+static int setdeflate __P((char **));
+static int setnodeflate __P((char **));
+static int setnodeflatedraft __P((char **));
+static int setdemand __P((char **));
+static int setpred1comp __P((char **));
+static int setnopred1comp __P((char **));
+static int setipparam __P((char **));
+static int setpapcrypt __P((char **));
+static int setidle __P((char **));
+static int setholdoff __P((char **));
+static int setdnsaddr __P((char **));
+static int resetipxproto __P((char **));
+static int setwinsaddr __P((char **));
+static int showversion __P((char **));
+static int showhelp __P((char **));
+
+#ifdef PPP_FILTER
+static int setpdebug __P((char **));
+static int setpassfilter __P((char **));
+static int setactivefilter __P((char **));
+#endif
+
+#ifdef IPX_CHANGE
+static int setipxproto __P((char **));
+static int setipxanet __P((char **));
+static int setipxalcl __P((char **));
+static int setipxarmt __P((char **));
+static int setipxnetwork __P((char **));
+static int setipxnode __P((char **));
+static int setipxrouter __P((char **));
+static int setipxname __P((char **));
+static int setipxcptimeout __P((char **));
+static int setipxcpterm __P((char **));
+static int setipxcpconf __P((char **));
+static int setipxcpfails __P((char **));
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+static int setmslanman __P((char **));
+#endif
+
+static int number_option __P((char *, u_int32_t *, int));
+static int int_option __P((char *, int *));
+static int readable __P((int fd));
+
+/*
+ * Valid arguments.
+ */
+static struct cmd {
+ char *cmd_name;
+ int num_args;
+ int (*cmd_func) __P((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 */
+ {"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 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 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..147ae38
--- /dev/null
+++ b/usr.sbin/pppd/pathnames.h
@@ -0,0 +1,32 @@
+/*
+ * 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 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..fdca2b5
--- /dev/null
+++ b/usr.sbin/pppd/pppd.8
@@ -0,0 +1,1202 @@
+.\" 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).
+.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.
+.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 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 conjuction 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 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 nuber 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 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/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..248556d
--- /dev/null
+++ b/usr.sbin/pppd/pppd.h
@@ -0,0 +1,493 @@
+/*
+ * 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
+
+/*
+ * 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) __P((int unit));
+ /* Process a received packet */
+ void (*input) __P((int unit, u_char *pkt, int len));
+ /* Process a received protocol-reject */
+ void (*protrej) __P((int unit));
+ /* Lower layer has come up */
+ void (*lowerup) __P((int unit));
+ /* Lower layer has gone down */
+ void (*lowerdown) __P((int unit));
+ /* Open the protocol */
+ void (*open) __P((int unit));
+ /* Close the protocol */
+ void (*close) __P((int unit, char *reason));
+ /* Print a packet in readable form */
+ int (*printpkt) __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+ /* Process a received data packet */
+ void (*datainput) __P((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) __P((void));
+ /* Configure interface for demand-dial */
+ int (*demand_conf) __P((int unit));
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt) __P((u_char *pkt, int len));
+};
+
+/* Table of pointers to supported protocols */
+extern struct protent *protocols[];
+
+/*
+ * Prototypes.
+ */
+
+/* Procedures exported from main.c. */
+void detach __P((void)); /* Detach from controlling tty */
+void die __P((int)); /* Cleanup and exit */
+void quit __P((void)); /* like die(1) */
+void novm __P((char *)); /* Say we ran out of memory, and die */
+void timeout __P((void (*func)(void *), void *arg, int t));
+ /* Call func(arg) after t seconds */
+void untimeout __P((void (*func)(void *), void *arg));
+ /* Cancel call to func(arg) */
+int run_program __P((char *prog, char **args, int must_exist));
+ /* Run program prog with args in child */
+void demuxprotrej __P((int, int));
+ /* Demultiplex a Protocol-Reject */
+void format_packet __P((u_char *, int, void (*) (void *, char *, ...),
+ void *)); /* Format a packet in human-readable form */
+void log_packet __P((u_char *, int, char *, int));
+ /* Format a packet and log it with syslog */
+void print_string __P((char *, int, void (*) (void *, char *, ...),
+ void *)); /* Format a string for output */
+int fmtmsg __P((char *, int, char *, ...)); /* sprintf++ */
+int vfmtmsg __P((char *, int, char *, va_list)); /* vsprintf++ */
+void script_setenv __P((char *, char *)); /* set script env var */
+void script_unsetenv __P((char *)); /* unset script env var */
+
+/* Procedures exported from auth.c */
+void link_required __P((int)); /* we are starting to use the link */
+void link_terminated __P((int)); /* we are finished with the link */
+void link_down __P((int)); /* the LCP layer has left the Opened state */
+void link_established __P((int)); /* the link is up; authenticate now */
+void np_up __P((int, int)); /* a network protocol has come up */
+void np_down __P((int, int)); /* a network protocol has gone down */
+void np_finished __P((int, int)); /* a network protocol no longer needs link */
+void auth_peer_fail __P((int, int));
+ /* peer failed to authenticate itself */
+void auth_peer_success __P((int, int, char *, int));
+ /* peer successfully authenticated itself */
+void auth_withpeer_fail __P((int, int));
+ /* we failed to authenticate ourselves */
+void auth_withpeer_success __P((int, int));
+ /* we successfully authenticated ourselves */
+void auth_check_options __P((void));
+ /* check authentication options supplied */
+void auth_reset __P((int)); /* check what secrets we have */
+int check_passwd __P((int, char *, int, char *, int, char **, int *));
+ /* Check peer-supplied username/password */
+int get_secret __P((int, char *, char *, char *, int *, int));
+ /* get "secret" for chap */
+int auth_ip_addr __P((int, u_int32_t));
+ /* check if IP address is authorized */
+int bad_ip_adrs __P((u_int32_t));
+ /* check if IP address is unreasonable */
+void check_access __P((FILE *, char *));
+ /* check permissions on secrets file */
+
+/* Procedures exported from demand.c */
+void demand_conf __P((void)); /* config interface(s) for demand-dial */
+void demand_block __P((void)); /* set all NPs to queue up packets */
+void demand_unblock __P((void)); /* set all NPs to pass packets */
+void demand_discard __P((void)); /* set all NPs to discard packets */
+void demand_rexmit __P((int)); /* retransmit saved frames for an NP */
+int loop_chars __P((unsigned char *, int)); /* process chars from loopback */
+int loop_frame __P((unsigned char *, int)); /* process frame from loopback */
+
+/* Procedures exported from sys-*.c */
+void sys_init __P((void)); /* Do system-dependent initialization */
+void sys_cleanup __P((void)); /* Restore system state before exiting */
+void sys_check_options __P((void)); /* Check options specified */
+void sys_close __P((void)); /* Clean up in a child before execing */
+int ppp_available __P((void)); /* Test whether ppp kernel support exists */
+void open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
+void establish_ppp __P((int)); /* Turn serial port into a ppp interface */
+void restore_loop __P((void)); /* Transfer ppp unit back to loopback */
+void disestablish_ppp __P((int)); /* Restore port to normal operation */
+void clean_check __P((void)); /* Check if line was 8-bit clean */
+void set_up_tty __P((int, int)); /* Set up port's speed, parameters, etc. */
+void restore_tty __P((int)); /* Restore port's original parameters */
+void setdtr __P((int, int)); /* Raise or lower port's DTR line */
+void output __P((int, u_char *, int)); /* Output a PPP packet */
+void wait_input __P((struct timeval *));
+ /* Wait for input, with timeout */
+void wait_loop_output __P((struct timeval *));
+ /* Wait for pkt from loopback, with timeout */
+void wait_time __P((struct timeval *)); /* Wait for given length of time */
+int read_packet __P((u_char *)); /* Read PPP packet */
+int get_loop_output __P((void)); /* Read pkts from loopback */
+void ppp_send_config __P((int, int, u_int32_t, int, int));
+ /* Configure i/f transmit parameters */
+void ppp_set_xaccm __P((int, ext_accm));
+ /* Set extended transmit ACCM */
+void ppp_recv_config __P((int, int, u_int32_t, int, int));
+ /* Configure i/f receive parameters */
+int ccp_test __P((int, u_char *, int, int));
+ /* Test support for compression scheme */
+void ccp_flags_set __P((int, int, int));
+ /* Set kernel CCP state */
+int ccp_fatal_error __P((int)); /* Test for fatal decomp error in kernel */
+int get_idle_time __P((int, struct ppp_idle *));
+ /* Find out how long link has been idle */
+int sifvjcomp __P((int, int, int, int));
+ /* Configure VJ TCP header compression */
+int sifup __P((int)); /* Configure i/f up (for IP) */
+int sifnpmode __P((int u, int proto, enum NPmode mode));
+ /* Set mode for handling packets for proto */
+int sifdown __P((int)); /* Configure i/f down (for IP) */
+int sifaddr __P((int, u_int32_t, u_int32_t, u_int32_t));
+ /* Configure IP addresses for i/f */
+int cifaddr __P((int, u_int32_t, u_int32_t));
+ /* Reset i/f IP addresses */
+int sifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Create default route through i/f */
+int cifdefaultroute __P((int, u_int32_t, u_int32_t));
+ /* Delete default route through i/f */
+int sifproxyarp __P((int, u_int32_t));
+ /* Add proxy ARP entry for peer */
+int cifproxyarp __P((int, u_int32_t));
+ /* Delete proxy ARP entry for peer */
+u_int32_t GetMask __P((u_int32_t)); /* Get appropriate netmask for address */
+int lock __P((char *)); /* Create lock file for device */
+void unlock __P((void)); /* Delete previously-created lock file */
+int daemon __P((int, int)); /* Detach us from terminal session */
+void logwtmp __P((const char *, const char *, const char *));
+ /* Write entry to wtmp file */
+int get_host_seed __P((void)); /* Get host-dependent random number seed */
+#ifdef PPP_FILTER
+int set_filters __P((struct bpf_program *pass, struct bpf_program *active));
+ /* Set filter programs in kernel */
+#endif
+
+/* Procedures exported from options.c */
+int parse_args __P((int argc, char **argv));
+ /* Parse options from arguments given */
+void usage __P((void)); /* Print a usage message */
+int options_from_file __P((char *filename, int must_exist, int check_prot,
+ int privileged));
+ /* Parse options from an options file */
+int options_from_user __P((void)); /* Parse options from user's .ppprc */
+int options_for_tty __P((void)); /* Parse options from /etc/ppp/options.tty */
+void scan_args __P((int argc, char **argv));
+ /* Look for tty name in command-line args */
+int getword __P((FILE *f, char *word, int *newlinep, char *filename));
+ /* Read a word from a file */
+void option_error __P((char *fmt, ...));
+ /* Print an error message about an option */
+int setipaddr __P((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 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..3ca43c0
--- /dev/null
+++ b/usr.sbin/pppd/sys-bsd.c
@@ -0,0 +1,1569 @@
+/*
+ * 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>
+
+#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 */
+
+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 __P((u_int32_t, int));
+static int get_ether_addr __P((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);
+ }
+}
+
+/*
+ * 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()
+{
+ close(sockfd);
+ 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);
+}
+
+
+/*
+ * 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..627c7d0
--- /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 __P((int));
+static void upap_lowerup __P((int));
+static void upap_lowerdown __P((int));
+static void upap_input __P((int, u_char *, int));
+static void upap_protrej __P((int));
+static int upap_printpkt __P((u_char *, int,
+ void (*) __P((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 __P((void *));
+static void upap_reqtimeout __P((void *));
+static void upap_rauthreq __P((upap_state *, u_char *, int, int));
+static void upap_rauthack __P((upap_state *, u_char *, int, int));
+static void upap_rauthnak __P((upap_state *, u_char *, int, int));
+static void upap_sauthreq __P((upap_state *));
+static void upap_sresp __P((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) __P((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..2cfd86c
--- /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 __P((int, char *, char *));
+void upap_authpeer __P((int));
+
+extern struct protent pap_protent;
diff --git a/usr.sbin/pppstats/Makefile b/usr.sbin/pppstats/Makefile
new file mode 100644
index 0000000..0aab78b
--- /dev/null
+++ b/usr.sbin/pppstats/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+#as per policies in handbook
+MAINTAINER= peter@freebsd.org
+
+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..d3b7b3d
--- /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 __P((void));
+static void catchalarm __P((int));
+static void get_ppp_stats __P((struct ppp_stats *));
+static void get_ppp_cstats __P((struct ppp_comp_stats *));
+static void intpr __P((void));
+
+int main __P((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..280e164
--- /dev/null
+++ b/usr.sbin/praliases/Makefile
@@ -0,0 +1,50 @@
+# @(#)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
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmdb)
+LIBSMDBDIR:= ${.OBJDIR}/../../lib/libsmdb
+.else
+LIBSMDBDIR!= cd ${.CURDIR}/../../lib/libsmdb; make -V .OBJDIR
+.endif
+LIBSMDB:= ${LIBSMDBDIR}/libsmdb.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+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/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..9d1dffe
--- /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?= 2
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8
new file mode 100644
index 0000000..f816222
--- /dev/null
+++ b/usr.sbin/pstat/pstat.8
@@ -0,0 +1,267 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)pstat.8 8.5 (Berkeley) 5/13/94
+.\" $FreeBSD$
+.\"
+.Dd May 23, 2002
+.Dt PSTAT 8
+.Os
+.Sh NAME
+.Nm pstat ,
+.Nm swapinfo
+.Nd display system data structures
+.Sh SYNOPSIS
+.Nm
+.Op Fl Tfknst
+.Op Fl M Ar core Op Fl N Ar system
+.Nm swapinfo
+.Op Fl k
+.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
+option is 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 k
+Print sizes in kilobytes, 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 RAW
+Number of characters in raw input queue.
+.It CAN
+Number of characters in canonicalized input queue.
+.It OUT
+Number of characters in output queue.
+.It MODE
+See
+.Xr tty 4 .
+.It ADDR
+Physical device address.
+.It DEL
+Number of delimiters (newlines) in canonicalized input queue.
+.It COL
+Calculated column position of terminal.
+.It STATE
+Miscellaneous state variables encoded thus:
+.Pp
+.Bl -tag -width indent -compact
+.It T
+delay timeout in progress
+.It W
+waiting for open to complete
+.It O
+open
+.It F
+outq has been flushed during DMA
+.It C
+carrier is on
+.It c
+connection open
+.It B
+busy doing output
+.It A
+process is waiting for space in output queue
+.It a
+process is waiting for output to complete
+.It X
+open for exclusive use
+.It S
+output stopped (ixon flow control)
+.It m
+output stopped (carrier flow control)
+.It o
+output stopped (CTS flow control)
+.It d
+output stopped (DSR flow control)
+.It K
+input stopped
+.It Y
+send SIGIO for input events
+.It D
+state for lowercase
+.Ql \e
+work
+.It E
+within a
+.Ql \e.../
+for PRTRUB
+.It L
+next character is literal
+.It P
+retyping suspended input (PENDIN)
+.It N
+counting tab width, ignore FLUSHO
+.It l
+block mode input routine in use
+.It s
+i/o being snooped
+.It Z
+connection lost
+.El
+.It SESS
+Kernel address of the session structure.
+.It PGID
+Process group for which this is controlling terminal.
+.It DISC
+Line discipline;
+.Ql term
+for
+TTYDISC
+or
+.Ql ntty
+for
+NTTYDISC
+or
+.Ql tab
+for
+TABLDISC
+or
+.Ql slip
+for
+SLIPDISC
+or
+.Ql ppp
+for
+PPPDISC.
+.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 BUGS
+Does not understand
+.Tn NFS
+swap servers.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c
new file mode 100644
index 0000000..7dd5fc2
--- /dev/null
+++ b/usr.sbin/pstat/pstat.c
@@ -0,0 +1,593 @@
+/*-
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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/ioctl_compat.h> /* XXX NTTYDISC is too well hidden */
+#include <sys/tty.h>
+#include <sys/blist.h>
+
+#include <sys/user.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.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[] = {
+ { "_constty", 0 },
+ { "_maxfiles", 0 },
+ { "_nfiles", 0 },
+ { "_tty_list", 0 },
+ { "" }
+};
+
+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 = "kM:N:";
+ usagestr = "swapinfo [-k] [-M core [-N system]]";
+ } else {
+ opts = "TM:N:fknst";
+ usagestr = "pstat [-Tfknst] [-M core [-N system]]";
+ }
+
+ while ((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case 'f':
+ fileflag = 1;
+ break;
+ case 'k':
+ putenv("BLOCKSIZE=1K");
+ 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 RAW CAN OUT IHIWT ILOWT OHWT LWT COL STATE SESS PGID DISC\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_rawcc = tty.t_rawq.c_cc;
+ xt.xt_cancc = tty.t_canq.c_cc;
+ xt.xt_outcc = tty.t_outq.c_cc;
+#define XT_COPY(field) xt.xt_##field = tty.t_##field
+ XT_COPY(line);
+ XT_COPY(state);
+ XT_COPY(column);
+ XT_COPY(ihiwat);
+ XT_COPY(ilowat);
+ XT_COPY(ohiwat);
+ XT_COPY(olowat);
+#undef XT_COPY
+ ttyprt(&xt);
+ tp = TAILQ_NEXT(tp, 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[] = {
+#ifdef TS_WOPEN
+ { TS_WOPEN, 'W'},
+#endif
+ { TS_ISOPEN, 'O'},
+ { TS_CARR_ON, 'C'},
+#ifdef TS_CONNECTED
+ { TS_CONNECTED, 'c'},
+#endif
+ { TS_TIMEOUT, 'T'},
+ { TS_FLUSH, 'F'},
+ { TS_BUSY, 'B'},
+#ifdef TS_ASLEEP
+ { TS_ASLEEP, 'A'},
+#endif
+#ifdef TS_SO_OLOWAT
+ { TS_SO_OLOWAT, 'A'},
+#endif
+#ifdef TS_SO_OCOMPLETE
+ { TS_SO_OCOMPLETE, 'a'},
+#endif
+ { TS_XCLUDE, 'X'},
+ { TS_TTSTOP, 'S'},
+#ifdef TS_CAR_OFLOW
+ { TS_CAR_OFLOW, 'm'},
+#endif
+#ifdef TS_CTS_OFLOW
+ { TS_CTS_OFLOW, 'o'},
+#endif
+#ifdef TS_DSR_OFLOW
+ { TS_DSR_OFLOW, 'd'},
+#endif
+ { TS_TBLOCK, 'K'},
+ { TS_ASYNC, 'Y'},
+ { TS_BKSL, 'D'},
+ { TS_ERASE, 'E'},
+ { TS_LNCH, 'L'},
+ { TS_TYPEN, 'P'},
+ { TS_CNTTB, 'N'},
+#ifdef TS_CAN_BYPASS_L_RINT
+ { TS_CAN_BYPASS_L_RINT, 'l'},
+#endif
+#ifdef TS_SNOOP
+ { TS_SNOOP, 's'},
+#endif
+#ifdef TS_ZOMBIE
+ { TS_ZOMBIE, 'Z'},
+#endif
+ { 0, '\0'},
+};
+
+static void
+ttyprt(struct xtty *xt)
+{
+ int i, j;
+ pid_t pgid;
+ char *name, state[20];
+
+ 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(" %2d,%-2d", major(xt->xt_dev), minor(xt->xt_dev));
+ else
+ (void)printf("%7s ", name);
+ (void)printf("%2ld %3ld ", xt->xt_rawcc, xt->xt_cancc);
+ (void)printf("%3ld %5d %5d %4d %3d %7d ", xt->xt_outcc,
+ xt->xt_ihiwat, xt->xt_ilowat, xt->xt_ohiwat, xt->xt_olowat,
+ xt->xt_column);
+ for (i = j = 0; ttystates[i].flag; i++)
+ if (xt->xt_state & ttystates[i].flag)
+ state[j++] = ttystates[i].val;
+ if (j == 0)
+ state[j++] = '-';
+ state[j] = '\0';
+ (void)printf("%-6s %8d", state, xt->xt_sid);
+ pgid = 0;
+ (void)printf("%6d ", xt->xt_pgid);
+ switch (xt->xt_line) {
+ case TTYDISC:
+ (void)printf("term\n");
+ break;
+ case NTTYDISC:
+ (void)printf("ntty\n");
+ break;
+ case SLIPDISC:
+ (void)printf("slip\n");
+ break;
+ case PPPDISC:
+ (void)printf("ppp\n");
+ break;
+ default:
+ (void)printf("%d\n", xt->xt_line);
+ break;
+ }
+}
+
+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) ((int)((intmax_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(struct kvm_swap *ksw)
+{
+ int hlen, pagesize;
+ long blocksize;
+
+ pagesize = getpagesize();
+ getbsize(&hlen, &blocksize);
+ swtot.ksw_total += ksw->ksw_total;
+ swtot.ksw_used += ksw->ksw_used;
+ ++nswdev;
+ if (totalflag == 0) {
+ (void)printf("%-15s %*d ",
+ ksw->ksw_devname, hlen,
+ CONVERT(ksw->ksw_total));
+ (void)printf("%8d %8d %5.0f%%\n",
+ CONVERT(ksw->ksw_used),
+ CONVERT(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("%dM/%dM swap space\n",
+ CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total));
+ } else if (nswdev > 1) {
+ (void)printf("%-15s %*d %8d %8d %5.0f%%\n",
+ "Total", hlen, CONVERT(swtot.ksw_total),
+ CONVERT(swtot.ksw_used),
+ CONVERT(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..d8fa42b
--- /dev/null
+++ b/usr.sbin/pw/cpdir.c
@@ -0,0 +1,127 @@
+/*-
+ * 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)
+{
+ int rc = 0;
+ char src[MAXPATHLEN];
+ char dst[MAXPATHLEN];
+
+ 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')
+ rc = 1;
+ else {
+ 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 (stat(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 */
+ /*
+ * 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..0220564
--- /dev/null
+++ b/usr.sbin/pw/pw.8
@@ -0,0 +1,968 @@
+.\" 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 January 11, 2004
+.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 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 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 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 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-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.
+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 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.
+.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.
+.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 DIAGNOSTICS
+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 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 SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.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..4010e84
--- /dev/null
+++ b/usr.sbin/pw/pw.c
@@ -0,0 +1,453 @@
+/*-
+ * 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:mk:s:oL:i:w:h:H:Db:NPy:Y",
+ "V:C:qn:u:rY",
+ "V:C:qn:u:c:d:e:p:g:G:ml: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:pNPY",
+ "V:C:qn:g:Y",
+ "V:C:qn: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
+ };
+
+ umask(0); /* We wish to handle this manually */
+ 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-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-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-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-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..729d45a
--- /dev/null
+++ b/usr.sbin/pw/pw.conf.5
@@ -0,0 +1,309 @@
+.\" 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 December 9, 1996
+.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 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 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 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..f541b26
--- /dev/null
+++ b/usr.sbin/pw/pw.h
@@ -0,0 +1,131 @@
+/*-
+ * 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;
+};
+
+extern LIST_HEAD(cargs, carg) arglist;
+
+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 */
+ 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..9377239
--- /dev/null
+++ b/usr.sbin/pw/pw_conf.c
@@ -0,0 +1,501 @@
+/*-
+ * 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_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 */
+ "/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# 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",
+ "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;
+
+ 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_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_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..1c63a11
--- /dev/null
+++ b/usr.sbin/pw/pw_group.c
@@ -0,0 +1,359 @@
+/*-
+ * 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 <unistd.h>
+
+#include "pw.h"
+#include "bitmap.h"
+
+
+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, '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 == 'm') {
+ int k = 0;
+
+ while (grp->gr_mem[k] != NULL) {
+ if (extendarray(&members, &grmembers, i + 2) != -1) {
+ members[i++] = grp->gr_mem[k];
+ }
+ k++;
+ }
+ }
+ for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
+ int j;
+ if ((pwd = GETPWNAM(p)) == NULL) {
+ if (!isdigit((unsigned char)*p) || (pwd = getpwuid((uid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "user `%s' does not exist", p);
+ }
+ /*
+ * Check for duplicates
+ */
+ 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;
+}
+
+
+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..5fd3671
--- /dev/null
+++ b/usr.sbin/pw/pw_user.c
@@ -0,0 +1,1313 @@
+/*-
+ * 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>
+#if defined(USE_MD5RAND)
+#include <md5.h>
+#endif
+#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;
+
+ 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 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, 0755) != -1 || errno == EEXIST) {
+ chown(dbuf, 0, 0);
+ symlink(dbuf, 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, 0755) == -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, 0755) == -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, " \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;
+ 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, 0755, 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);
+}
+
+static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
+
+char *
+pw_pwcrypt(char *password)
+{
+ int i;
+ char salt[12];
+
+ static char buf[256];
+
+ /*
+ * Calculate a salt value
+ */
+ for (i = 0; i < 8; i++)
+ salt[i] = chars[arc4random() % 63];
+ salt[i] = '\0';
+
+ return strcpy(buf, crypt(password, salt));
+}
+
+#if defined(USE_MD5RAND)
+u_char *
+pw_getrand(u_char *buf, int len) /* cryptographically secure rng */
+{
+ int i;
+ for (i=0;i<len;i+=16) {
+ u_char ubuf[16];
+
+ MD5_CTX md5_ctx;
+ struct timeval tv, tvo;
+ struct rusage ru;
+ int n=0;
+ int t;
+
+ MD5Init (&md5_ctx);
+ t=getpid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ t=getppid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ gettimeofday (&tvo, NULL);
+ do {
+ getrusage (RUSAGE_SELF, &ru);
+ MD5Update (&md5_ctx, (u_char*)&ru, sizeof ru);
+ gettimeofday (&tv, NULL);
+ MD5Update (&md5_ctx, (u_char*)&tv, sizeof tv);
+ } while (n++<20 || tv.tv_usec-tvo.tv_usec<100*1000);
+ MD5Final (ubuf, &md5_ctx);
+ memcpy(buf+i, ubuf, MIN(16, len-i));
+ }
+ return buf;
+}
+
+#else /* Portable version */
+
+static u_char *
+pw_getrand(u_char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ unsigned long val = arc4random();
+ /* Use all bits in the random value */
+ buf[i]=(u_char)((val >> 24) ^ (val >> 16) ^ (val >> 8) ^ val);
+ }
+ return buf;
+}
+
+#endif
+
+static char *
+pw_password(struct userconf * cnf, struct cargs * args, char const * user)
+{
+ int i, l;
+ char pwbuf[32];
+ u_char rndbuf[sizeof pwbuf];
+
+ switch (cnf->default_password) {
+ case -1: /* Random password */
+ l = (arc4random() % 8 + 8); /* 8 - 16 chars */
+ pw_getrand(rndbuf, l);
+ for (i = 0; i < l; i++)
+ pwbuf[i] = chars[rndbuf[i] % (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..84226a9
--- /dev/null
+++ b/usr.sbin/pw/pwupd.c
@@ -0,0 +1,212 @@
+/*-
+ * 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
+ if (pwdb("-C", NULL) == 0) { /* Check only */
+#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, 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..e3dc642
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.8
@@ -0,0 +1,170 @@
+.\" 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. 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.
+.\"
+.\" @(#)pwd_mkdb.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt PWD_MKDB 8
+.Os
+.Sh NAME
+.Nm pwd_mkdb
+.Nd "generate the password databases"
+.Sh SYNOPSIS
+.Nm
+.Op Fl C
+.Op Fl N
+.Op Fl p
+.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 C
+Check if the password file is in the correct format.
+Do not
+change, add, or remove any files.
+.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 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 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.
+.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
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.c b/usr.sbin/pwd_mkdb/pwd_mkdb.c
new file mode 100644
index 0000000..4991715
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c
@@ -0,0 +1,757 @@
+/*-
+ * 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 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/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() */
+ 0 /* 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;
+ int32_t pw_change, pw_expire;
+ 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;
+ int nblock = 0;
+
+ Cflag = 0;
+ strcpy(prefix, _PATH_PWD);
+ makeold = 0;
+ username = NULL;
+ while ((ch = getopt(argc, argv, "Cd:ps:u:vN")) != -1)
+ switch(ch) {
+ case 'C': /* verify only */
+ Cflag = 1;
+ break;
+ case 'd':
+ strncpy(prefix, optarg, sizeof prefix - 1);
+ 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;
+ case 'N': /* do not wait for lock */
+ nblock = LOCK_NB;
+ 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)
+ 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);
+ if (!is_comment &&
+ (!username || (strcmp(username, pwd.pw_name) == 0))) {
+ pw_change = pwd.pw_change;
+ pw_expire = pwd.pw_expire;
+ /* 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("*");
+ memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
+ p += sizeof(int);
+ memmove(p, &pw_change, sizeof(pw_change));
+ p += sizeof(pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pw_expire, sizeof(pw_expire));
+ p += sizeof(pw_expire);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof pwd.pw_fields;
+ data.size = p - buf;
+
+ /* Create secure data. (legacy version) */
+ p = sbuf;
+ COMPACT(pwd.pw_name);
+ COMPACT(pwd.pw_passwd);
+ memmove(p, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(pwd.pw_gid));
+ p += sizeof(int);
+ memmove(p, &pw_change, sizeof(pw_change));
+ p += sizeof(pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pw_expire, sizeof(pw_expire));
+ p += sizeof(pw_expire);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof 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);
+ memmove(tbuf + 1, &cnt, sizeof(cnt));
+ key.size = sizeof(cnt) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by uid. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
+ memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ key.size = sizeof(pwd.pw_uid) + 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);
+ memmove(tbuf + 1, &cnt, sizeof(cnt));
+ key.size = sizeof(cnt) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by uid. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
+ memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ key.size = sizeof(pwd.pw_uid) + 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);
+ memmove(tbuf + 1, &ypcnt, sizeof(cnt));
+ ypcnt++;
+ key.size = sizeof(cnt) + 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(fp, pw)
+ FILE *fp;
+ struct passwd *pw;
+{
+ static int lcnt;
+ char *p;
+
+ if (!fgets(line, sizeof(line), fp))
+ return (0);
+ ++lcnt;
+ /*
+ * ``... if I swallow anything evil, put your fingers down my
+ * throat...''
+ * -- The Who
+ */
+ if (!(p = strchr(line, '\n'))) {
+ /*
+ * XXX: This may also happen if the last line in a
+ * file does not have a trailing newline.
+ */
+ warnx("line #%d too long", lcnt);
+ goto fmt;
+
+ }
+ *p = '\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(from, to, mode)
+ char *from, *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(from, to)
+ char *from, *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(name)
+ const char *name;
+{
+
+ warn("%s", name);
+ cleanup();
+ exit(1);
+}
+
+void
+cleanup()
+{
+ 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)fprintf(stderr,
+"usage: pwd_mkdb [-C] [-N] [-p] [-d <dest dir>] [-s <cachesize>] [-u <local 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..ffa9971
--- /dev/null
+++ b/usr.sbin/quot/quot.8
@@ -0,0 +1,109 @@
+.\" 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 BUGS
+.Xr ncheck
+(which would be a lot more useful than
+.Nm ls Fl i
+in the example above)
+does not exist in
+.Fx .
+.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.
diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c
new file mode 100644
index 0000000..75e758b
--- /dev/null
+++ b/usr.sbin/quot/quot.c
@@ -0,0 +1,650 @@
+/*
+ * 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(fd,super,ino)
+ int fd;
+ struct fs *super;
+ ino_t ino;
+{
+ static caddr_t ipbuf;
+ static ino_t last;
+
+ if (fd < 0) { /* flush cache */
+ if (ipbuf) {
+ free(ipbuf);
+ ipbuf = 0;
+ }
+ return 0;
+ }
+
+ if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
+ 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)]);
+ return ((union dinode *)
+ &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]);
+}
+
+#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(super, dp)
+ struct fs *super;
+ union dinode *dp;
+{
+ register 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(super, dp)
+ 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;
+ default:
+ return 1;
+ }
+#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()
+{
+ register int i;
+ register 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()
+{
+ register int i;
+ register 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)
+ uid_t uid;
+{
+ register struct user *usr;
+ register 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(v1,v2)
+ const void *v1, *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,blks,act)
+ uid_t uid;
+ daddr_t blks;
+ time_t act;
+{
+ static time_t today;
+ register 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()
+{
+ register struct fsizes *fp;
+ register 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(fd, super, name)
+ int fd;
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ union dinode *dp;
+ daddr_t sz, ksz;
+ struct fsizes *fp, **fsp;
+ register 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(fd, super, name)
+ int fd;
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ struct user *usr, *usrs;
+ union dinode *dp;
+ register 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(fd, super, name)
+ int fd;
+ struct fs *super;
+ char *name;
+{
+ int c;
+ ino_t inode, inode1;
+ 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);
+ inode1 = -1;
+ 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');
+ inode1 = inode;
+ } else {
+ if (errno) {
+ err(1, "%s", name);
+ }
+ /* skip this line */
+ while ((c = getchar()) != EOF && c != '\n');
+ }
+ if (c == EOF)
+ break;
+ }
+}
+
+static void
+usage()
+{
+#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(name,mp)
+ char *name, *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(argc,argv)
+ int argc;
+ char **argv;
+{
+ char all = 0;
+ struct statfs *mp;
+ struct fstab *fs;
+ char dev[MNAMELEN + 1];
+ char *nm;
+ 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)) {
+ if ((nm = strrchr(mp->f_mntfromname,'/'))) {
+ sprintf(dev,"%s%s",_PATH_DEV,nm + 1);
+ nm = dev;
+ } else
+ nm = mp->f_mntfromname;
+ quot(nm,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..09c7718
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.8
@@ -0,0 +1,141 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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..3ade757
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.c
@@ -0,0 +1,264 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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)
+{
+ register 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()
+{
+
+ 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\n", fs->fs_file,
+ qfextension[type]);
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+ register char *target, *list[];
+ int cnt;
+{
+ register 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(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(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;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
+
+/*
+ * Verify filesystem is mounted and not readonly.
+ */
+int
+readonly(fs)
+ register 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..88c1eeb
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.8
@@ -0,0 +1,151 @@
+.\" @(#) $FreeBSD$ (LBL)
+.\"
+.\" 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, and (3) all advertising materials mentioning
+.\" features or use of this software display the following acknowledgement:
+.\" ``This product includes software developed by the University of California,
+.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+.\" the University nor the names 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.
+.\"
+.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..a5d745b
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.c
@@ -0,0 +1,956 @@
+/*
+ * 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, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names 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.
+ */
+
+#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..9cbe5a2
--- /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 aren't 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..81ebd9d
--- /dev/null
+++ b/usr.sbin/repquota/repquota.8
@@ -0,0 +1,110 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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 SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8
+.Sh DIAGNOSTICS
+Various messages about inaccessible files; self-explanatory.
+.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..7c0e22d
--- /dev/null
+++ b/usr.sbin/repquota/repquota.c
@@ -0,0 +1,412 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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 <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)
+{
+ register struct fstab *fs;
+ register struct passwd *pw;
+ register 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()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: repquota [-v] [-g] [-n] [-u] -a",
+ " repquota [-v] [-g] [-n] [-u] filesystem ...");
+ exit(1);
+}
+
+int
+repquota(fs, type, qfpathname)
+ register struct fstab *fs;
+ int type;
+ char *qfpathname;
+{
+ register 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("User%*s used soft hard grace used soft hard grace\n",
+ 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(target, list, cnt)
+ register char *target, *list[];
+ int cnt;
+{
+ register 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(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(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;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
+
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(id, type)
+ u_long id;
+ int type;
+{
+ register 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(id, type, name)
+ 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(seconds)
+ 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..51438a1
--- /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+= -DINET6 -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..931aa46
--- /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..90d65aa
--- /dev/null
+++ b/usr.sbin/rip6query/rip6query.c
@@ -0,0 +1,208 @@
+/* $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"
+
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#endif
+
+int s;
+struct sockaddr_in6 sin6;
+struct rip6 *ripbuf;
+
+#define RIPSIZE(n) (sizeof(struct rip6) + (n-1) * sizeof(struct netinfo6))
+
+int main __P((int, char **));
+static void usage __P((void));
+static const char *sa_n2a __P((struct sockaddr *));
+static const char *inet6_n2a __P((struct in6_addr *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct netinfo6 *np;
+ struct sockaddr_in6 fsock;
+ int i, n, len, flen;
+ int c;
+ int ifidx = -1;
+ int error;
+ 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 | NI_WITHSCOPEID) != 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..ed7dcb2
--- /dev/null
+++ b/usr.sbin/rmt/rmt.8
@@ -0,0 +1,222 @@
+.\" 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.
+.\"
+.\" @(#)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 rexec 3
+or
+.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 rexec 3 ,
+.Xr mtio 4 ,
+.Xr rdump 8 ,
+.Xr rrestore 8
+.Sh BUGS
+People should be discouraged from using this for a remote
+file access protocol.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/rmt/rmt.c b/usr.sbin/rmt/rmt.c
new file mode 100644
index 0000000..75b1694
--- /dev/null
+++ b/usr.sbin/rmt/rmt.c
@@ -0,0 +1,258 @@
+/*
+ * 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 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/route6d/Makefile b/usr.sbin/route6d/Makefile
new file mode 100644
index 0000000..1bed21a
--- /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+= -DINET6 -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..015e8c7
--- /dev/null
+++ b/usr.sbin/route6d/route6d.8
@@ -0,0 +1,253 @@
+.\" $FreeBSD$
+.\" $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.
+.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..f62c7f1
--- /dev/null
+++ b/usr.sbin/route6d/route6d.c
@@ -0,0 +1,3559 @@
+/* $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 __P((int, char **));
+void sighandler __P((int));
+void ripalarm __P((void));
+void riprecv __P((void));
+void ripsend __P((struct ifc *, struct sockaddr_in6 *, int));
+int out_filter __P((struct riprt *, struct ifc *));
+void init __P((void));
+void sockopt __P((struct ifc *));
+void ifconfig __P((void));
+void ifconfig1 __P((const char *, const struct sockaddr *, struct ifc *, int));
+void rtrecv __P((void));
+int rt_del __P((const struct sockaddr_in6 *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *));
+int rt_deladdr __P((struct ifc *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *));
+void filterconfig __P((void));
+int getifmtu __P((int));
+const char *rttypes __P((struct rt_msghdr *));
+const char *rtflags __P((struct rt_msghdr *));
+const char *ifflags __P((int));
+int ifrt __P((struct ifc *, int));
+void ifrt_p2p __P((struct ifc *, int));
+void applymask __P((struct in6_addr *, struct in6_addr *));
+void applyplen __P((struct in6_addr *, int));
+void ifrtdump __P((int));
+void ifdump __P((int));
+void ifdump0 __P((FILE *, const struct ifc *));
+void rtdump __P((int));
+void rt_entry __P((struct rt_msghdr *, int));
+void rtdexit __P((void));
+void riprequest __P((struct ifc *, struct netinfo6 *, int,
+ struct sockaddr_in6 *));
+void ripflush __P((struct ifc *, struct sockaddr_in6 *));
+void sendrequest __P((struct ifc *));
+int sin6mask2len __P((const struct sockaddr_in6 *));
+int mask2len __P((const struct in6_addr *, int));
+int sendpacket __P((struct sockaddr_in6 *, int));
+int addroute __P((struct riprt *, const struct in6_addr *, struct ifc *));
+int delroute __P((struct netinfo6 *, struct in6_addr *));
+struct in6_addr *getroute __P((struct netinfo6 *, struct in6_addr *));
+void krtread __P((int));
+int tobeadv __P((struct riprt *, struct ifc *));
+char *allocopy __P((char *));
+char *hms __P((void));
+const char *inet6_n2p __P((const struct in6_addr *));
+struct ifac *ifa_match __P((const struct ifc *, const struct in6_addr *, int));
+struct in6_addr *plen2mask __P((int));
+struct riprt *rtsearch __P((struct netinfo6 *, struct riprt **));
+int ripinterval __P((int));
+time_t ripsuptrig __P((void));
+void fatal __P((const char *, ...))
+ __attribute__((__format__(__printf__, 1, 2)));
+void trace __P((int, const char *, ...))
+ __attribute__((__format__(__printf__, 2, 3)));
+void tracet __P((int, const char *, ...))
+ __attribute__((__format__(__printf__, 2, 3)));
+unsigned int if_maxindex __P((void));
+struct ifc *ifc_find __P((char *));
+struct iff *iff_find __P((struct ifc *, int));
+void setindex2ifc __P((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
+
+ 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;
+ 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 &&
+ cm->cmsg_type == IPV6_PKTINFO) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ idx = pi->ipi6_ifindex;
+ 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;
+ }
+
+ 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, "Packets from non-ll addr: %s\n",
+ inet6_n2p(&fsock.sin6_addr));
+ return; /* Ignore packets from non-link-local addr */
+ }
+ 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_RESOLVE:
+ 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_RESOLVE:
+ 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;
+ if (ifa->ifa_plen == 128)
+ rrt->rrt_flags = RTF_HOST;
+ else
+ rrt->rrt_flags = RTF_CLONING;
+ 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("RESOLVE", RTM_RESOLVE);
+ 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
+ RTFLAG("C", RTF_CLONING);
+#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);
+ RTFLAG("L", RTF_LLINFO);
+ 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_CLONING|RTF_XRESOLVE|RTF_LLINFO|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..6bcbef2
--- /dev/null
+++ b/usr.sbin/rpc.lockd/kern.c
@@ -0,0 +1,607 @@
+/*-
+ * 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
+ * $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 <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"
+
+#define nfslockdans(_v, _ansp) \
+ ((_ansp)->la_vers = _v, \
+ nfsclnt(NFSCLNT_LOCKDANS, _ansp))
+
+/* 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 void client_cleanup(void);
+static void set_auth(CLIENT *cl, struct xucred *ucred);
+int lock_request(LOCKD_MSG *);
+int test_request(LOCKD_MSG *);
+void show(LOCKD_MSG *);
+int unlock_request(LOCKD_MSG *);
+
+/*
+ * 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);
+ (void)unlink(_PATH_LCKFIFO);
+ exit(-1);
+}
+
+/*
+ * client_request --
+ * Loop around messages from the kernel, forwarding them off to
+ * NLM servers.
+ */
+pid_t
+client_request(void)
+{
+ LOCKD_MSG msg;
+ fd_set rdset;
+ int fd, nr, ret;
+ pid_t child;
+ uid_t daemon_uid;
+ mode_t old_umask;
+ struct passwd *pw;
+
+ /* Recreate the NLM fifo. */
+ (void)unlink(_PATH_LCKFIFO);
+ old_umask = umask(S_IXGRP|S_IXOTH);
+ if (mkfifo(_PATH_LCKFIFO, S_IWUSR | S_IRUSR)) {
+ syslog(LOG_ERR, "mkfifo: %s: %m", _PATH_LCKFIFO);
+ exit (1);
+ }
+ umask(old_umask);
+
+ /*
+ * 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:
+ break;
+ default:
+ 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);
+
+ /* Open the fifo for reading. */
+ if ((fd = open(_PATH_LCKFIFO, O_RDONLY | O_NONBLOCK)) == -1) {
+ syslog(LOG_ERR, "open: %s: %m", _PATH_LCKFIFO);
+ goto err;
+ }
+ 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 (;;) {
+ /* Wait for contact... fifo's return EAGAIN when read with
+ * no data
+ */
+ /* Set up the select. */
+ FD_ZERO(&rdset);
+ FD_SET(fd, &rdset);
+ (void)select(fd + 1, &rdset, NULL, NULL, NULL);
+
+ /* Read the fixed length message. */
+ if ((nr = read(fd, &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_LCKFIFO);
+ goto err;
+ }
+ } else if (nr != 0) {
+ syslog(LOG_ERR,
+ "%s: discard %d bytes", _PATH_LCKFIFO, nr);
+ }
+ }
+
+ /* Reached only on error. */
+err:
+ (void)lockd_seteuid(0);
+ (void)unlink(_PATH_LCKFIFO);
+ _exit (1);
+}
+
+void
+set_auth(cl, xucred)
+ CLIENT *cl;
+ struct xucred *xucred;
+{
+ 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],
+ xucred->cr_ngroups - 1,
+ &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..076f0ee7
--- /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 <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 */
+
+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 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.
+ */
+ if (getnameinfo(host_addr, host_addr->sa_len, host, sizeof host,
+ NULL, 0, NI_NUMERICHOST) != 0) {
+ syslog(LOG_ERR, "unable to get name string for caller");
+ 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;
+{
+ memcpy(arg4, arg, sizeof(nlm_lock));
+ 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,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+
+ 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,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ 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,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ 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,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ 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,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+
+ 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,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ 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,
+ (struct sockaddr *)svc_getcaller(rqstp->rq_xprt));
+ 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,
+ (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf);
+ 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..ff00c28
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.c
@@ -0,0 +1,278 @@
+/* $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/types.h>
+#include <sys/socket.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 <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;
+pid_t client_pid;
+struct mon mon_host;
+
+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 usage(void);
+
+void sigalarm_handler(void);
+
+const char *transports[] = { "udp", "tcp", "udp6", "tcp6" };
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *transp;
+ int ch, i, maxindex, s;
+ struct sigaction sigalarm;
+ int grace_period = 30;
+ struct netconfig *nconf;
+ int maxrec = RPC_MAXDATASIZE;
+
+ while ((ch = getopt(argc, argv, "d:g:")) != (-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;
+ default:
+ case '?':
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (geteuid()) { /* This command allowed only to root */
+ fprintf(stderr, "Sorry. You are not superuser\n");
+ exit(1);
+ }
+
+ (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)
+ maxindex = 2;
+ else {
+ close(s);
+ maxindex = 4;
+ }
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ for (i = 0; i < maxindex; i++) {
+ nconf = getnetconfigent(transports[i]);
+ if (nconf == NULL)
+ errx(1, "cannot get %s netconf: %s.", transports[i],
+ nc_sperror());
+
+ transp = svc_tli_create(RPC_ANYFD, nconf, NULL,
+ RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (transp == NULL) {
+ errx(1, "cannot create %s service.", transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_SM, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERS, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERSX, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, nconf)) {
+ errx(1, "unable to register (NLM_PROG, NLM_VERS4, %s)",
+ transports[i]);
+ /* NOTREACHED */
+ }
+ freenetconfigent(nconf);
+ }
+
+ /*
+ * 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, 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);
+ }
+ grace_expired = 0;
+ alarm(10);
+
+ init_nsm();
+
+ client_pid = client_request();
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+void
+sigalarm_handler(void)
+{
+
+ grace_expired = 1;
+}
+
+void
+usage()
+{
+ errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>]");
+}
+
+/*
+ * 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 */
+}
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..9779aa4
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd_lock.c
@@ -0,0 +1,2262 @@
+/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * 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.
+ *
+ */
+
+#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
+
+/*
+ * SM_MAXSTRLEN is usually 1024. This means that lock requests and
+ * host name monitoring entries are *MUCH* larger than they should be
+ */
+
+/*
+ * 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 */
+ char client_name[SM_MAXSTRLEN];
+ 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 */
+ pid_t locker; /* pid of the child process trying to get the lock */
+ int fd; /* file descriptor for this lock */
+};
+
+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;
+ char name[SM_MAXSTRLEN];
+ int refcnt;
+};
+/* 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);
+void deallocate_file_lock(struct file_lock *fl);
+void fill_file_lock(struct file_lock *fl, const fhandle_t *fh,
+ struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len, const char *caller_name,
+ 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);
+
+
+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 locker: %d"
+ " fd: %d\n", fl->nsm_status, fl->status,
+ fl->flags, fl->locker, fl->fd);
+#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;
+}
+
+
+/*
+ * allocate_file_lock: Create a lock with the given parameters
+ */
+
+struct file_lock *
+allocate_file_lock(const netobj *lockowner, const netobj *matchcookie)
+{
+ struct file_lock *newfl;
+
+ newfl = malloc(sizeof(struct file_lock));
+ if (newfl == NULL) {
+ return NULL;
+ }
+ bzero(newfl, sizeof(newfl));
+
+ 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);
+
+ return newfl;
+}
+
+/*
+ * file_file_lock: Force creation of a valid file lock
+ */
+void
+fill_file_lock(struct file_lock *fl, const fhandle_t *fh,
+ struct sockaddr *addr, const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len, const char *caller_name,
+ const int state, const int status, const int flags, const int blocking)
+{
+ bcopy(fh, &fl->filehandle, sizeof(fhandle_t));
+ fl->addr = addr;
+
+ fl->client.exclusive = exclusive;
+ fl->client.svid = svid;
+ fl->client.l_offset = offset;
+ fl->client.l_len = len;
+
+ strncpy(fl->client_name, caller_name, SM_MAXSTRLEN);
+
+ 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->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 */
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ } else {
+ lflags = LEDGE_INSIDE;
+ }
+
+ 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 */
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ } else if (startu > starte) {
+ lflags = LEDGE_INSIDE;
+ }
+
+ /* 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 */
+ 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 */
+ 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 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("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("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("matching_unlock: Duplicate lock id. Granting\n");
+ return (ifl);
+ }
+
+ debuglog("Exiting 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);
+ 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->addr,
+ exist_lock->client.exclusive, exist_lock->client.svid,
+ start1, len1,
+ exist_lock->client_name, 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);
+ 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->addr,
+ exist_lock->client.exclusive, exist_lock->client.svid,
+ start2, len2,
+ exist_lock->client_name, 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;
+
+ printf("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
+ */
+
+void
+add_blockingfilelock(struct file_lock *fl)
+{
+
+ debuglog("Entering add_blockingfilelock\n");
+
+ /*
+ * 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\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, *pfl; /* Iterator */
+ enum partialfilelock_status pflstatus;
+
+ debuglog("Entering retry_blockingfilelocklist\n");
+
+ pfl = NULL;
+ ifl = LIST_FIRST(&blockedlocklist_head);
+ debuglog("Iterator choice %p\n",ifl);
+
+ while (ifl != NULL) {
+ /*
+ * SUBTLE BUG: The next element must be worked out before the
+ * current element has been moved
+ */
+ nfl = LIST_NEXT(ifl, nfslocklist);
+ debuglog("Iterator choice %p\n",ifl);
+ debuglog("Prev iterator choice %p\n",pfl);
+ 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 same place in blocked list */
+ debuglog("Replacing blocked lock\n");
+ if (pfl != NULL)
+ LIST_INSERT_AFTER(pfl, ifl, nfslocklist);
+ else
+ /* ifl is the only elem. in the list */
+ LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist);
+ }
+
+ /* Valid increment behavior regardless of state of ifl */
+ ifl = nfl;
+ /* if a lock was granted incrementing pfl would make it nfl */
+ if (pfl != NULL && (LIST_NEXT(pfl, nfslocklist) != nfl))
+ pfl = LIST_NEXT(pfl, nfslocklist);
+ else
+ pfl = LIST_FIRST(&blockedlocklist_head);
+ }
+
+ 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);
+ 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,
+ (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf,
+ lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset,
+ lckarg->alock.l_len,
+ lckarg->alock.caller_name, 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;
+
+ 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 */
+ nhp = malloc(sizeof(struct host));
+
+ if (nhp == NULL) {
+ debuglog("Unable to allocate entry for statd mon\n");
+ return;
+ }
+
+ /* Allocated new host entry, now fill the fields */
+ strncpy(nhp->name, hostname, SM_MAXSTRLEN);
+ 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\0";
+
+ 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");
+ sleep(1);
+ debuglog("Blowing off return send\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..da2ad74
--- /dev/null
+++ b/usr.sbin/rpc.lockd/rpc.lockd.8
@@ -0,0 +1,125 @@
+.\" $NetBSD: rpc.lockd.8,v 1.5 2000/06/09 18:51:47 cgd 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 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.
+.\"
+.\"
+.Dd September 24, 1995
+.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
+.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.
+.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 BUGS
+The current implementation serialises locks requests that could be shared.
+.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.
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..991af88
--- /dev/null
+++ b/usr.sbin/rpc.statd/file.c
@@ -0,0 +1,331 @@
+/*
+ * 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 <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;
+ int i;
+
+ for (i = 0, hp = status_info->hosts; i < status_info->noOfHosts; i++, hp++)
+ {
+ if (!strncasecmp(hostname, hp->hostname, SM_MAXSTRLEN))
+ {
+ result = hp;
+ break;
+ }
+ if (!spare_slot && !hp->monList && !hp->notifyReqd)
+ spare_slot = hp;
+ }
+
+ /* 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)
+ warn("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..eed7849
--- /dev/null
+++ b/usr.sbin/rpc.statd/procs.c
@@ -0,0 +1,424 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#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"
+
+/* 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_in *claddr;
+ char *dst;
+
+ len = strlen(arg);
+ dstlen = (4 * len) + 1;
+ dst = malloc(dstlen);
+ claddr = svc_getcaller(req->rq_xprt);
+ 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.",
+ inet_ntoa(claddr->sin_addr),
+ 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_in *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 = svc_getcaller(req->rq_xprt);
+ syslog(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
+ inet_ntoa(claddr->sin_addr), 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.mon_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;
+
+ 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..730f195
--- /dev/null
+++ b/usr.sbin/rpc.statd/rpc.statd.8
@@ -0,0 +1,109 @@
+.\" -*- 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 September 19, 1995
+.Dt RPC.STATD 8
+.Os
+.Sh NAME
+.Nm rpc.statd
+.Nd host status monitoring daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.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.
+.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 BUGS
+There is no means for the daemon to tell when a monitored host has
+disappeared permanently (eg. 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.
+.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
diff --git a/usr.sbin/rpc.statd/statd.c b/usr.sbin/rpc.statd/statd.c
new file mode 100644
index 0000000..507a027
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.c
@@ -0,0 +1,140 @@
+/*
+ * 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/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include "statd.h"
+
+int debug = 0; /* Controls syslog() calls for debug messages */
+
+extern void sm_prog_1(struct svc_req *rqstp, SVCXPRT *transp);
+static void handle_sigchld(int sig);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sa;
+ int ch;
+ int maxrec = RPC_MAXDATASIZE;
+
+ while ((ch = getopt(argc, argv, "d")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ (void)rpcb_unset(SM_PROG, SM_VERS, NULL);
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ if (!svc_create(sm_prog_1, SM_PROG, SM_VERS, "udp"))
+ errx(1, "cannot create udp service");
+ if (!svc_create(sm_prog_1, SM_PROG, SM_VERS, "tcp"))
+ errx(1, "cannot create tcp service");
+ 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);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rpc.statd [-d]\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));
+}
+
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..81cdd48
--- /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 isn't 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 HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.0 .
+.Sh SEE ALSO
+.Xr mountd 8 ,
+.Xr mount_nfs 8 ,
+.Xr umount 8
+.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..0ce1ce2
--- /dev/null
+++ b/usr.sbin/rpc.umntall/rpc.umntall.c
@@ -0,0 +1,264 @@
+/*
+ * 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;
+
+ clp = clnt_create(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp");
+ if (clp == NULL) {
+ warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT"));
+ return (0);
+ }
+ clp->cl_auth = authunix_create_default();
+ try.tv_sec = 3;
+ try.tv_usec = 0;
+ 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;
+
+ clp = clnt_create(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp");
+ if (clp == NULL) {
+ warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT"));
+ return (0);
+ }
+ clp->cl_auth = authsys_create_default();
+ try.tv_sec = 3;
+ try.tv_usec = 0;
+ 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..bc2269b
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/Makefile
@@ -0,0 +1,64 @@
+# $FreeBSD$
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv ${.CURDIR}/../../usr.bin/chpass \
+ ${.CURDIR}/../../libexec/ypxfr ${RPCDIR}
+
+PROG= rpc.yppasswdd
+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+= -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}
+
+afterinstall:
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/yppwupdate \
+ ${DESTDIR}/usr/libexec/yppwupdate
+
+.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..ba85e57
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
@@ -0,0 +1,358 @@
+.\" 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 don't 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's
+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 can't 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 don't 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 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.
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
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..ae917be
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
@@ -0,0 +1,350 @@
+/*
+ * 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>
+struct dom_binding {};
+#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;
+ int asize = sizeof (saddr);
+ struct netconfig *nconf;
+ 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) {
+ 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);
+ _rpcpmstart = 1;
+ }
+
+ if (!debug && _rpcpmstart == 0) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ }
+ openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON);
+
+ 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..04adf31
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
@@ -0,0 +1,900 @@
+/*
+ * 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
+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;
+
+ 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;
+ }
+ if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw, 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) {
+ if ((rval = update_inplace((struct passwd *)&argp->newpw,
+ 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..a578061
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ypserv ${.CURDIR}/../../libexec/ypxfr
+
+PROG= rpc.ypupdated
+NOMAN= yes
+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..5cc0d7a
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/update.c
@@ -0,0 +1,331 @@
+/*
+ * 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..225c7cb
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
@@ -0,0 +1,147 @@
+/*
+ * 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>
+struct dom_binding {};
+#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..ad8c5f4
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8
@@ -0,0 +1,150 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the 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
+.Xr securenets 5
+database permit it.
+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 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 can't be read on a little endian system.
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
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..f597ce2
--- /dev/null
+++ b/usr.sbin/rpcbind/Makefile
@@ -0,0 +1,21 @@
+# $NetBSD: Makefile,v 1.3 2000/06/20 13:56:43 fvdl Exp $
+# $FreeBSD$
+
+LIBCDIR= ${.CURDIR}/../../lib/libc
+LIBCRPCDIR= ${LIBCDIR}/rpc
+LIBCINCLUDE= ${LIBCDIR}/include
+
+.PATH: ${LIBCRPCDIR}
+
+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 \
+ rpc_generic.c
+
+CFLAGS+= -I${LIBCRPCDIR} -I${LIBCINCLUDE} -DPORTMAP -DINET6 -DLIBWRAP
+
+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..192c9ca
--- /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 __P((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..d74b369
--- /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 __P((rpcprog_t, rpcvers_t,
+ rpcprot_t));
+static bool_t pmapproc_change __P((struct svc_req *, SVCXPRT *, u_long));
+static bool_t pmapproc_getport __P((struct svc_req *, SVCXPRT *));
+static bool_t pmapproc_dump __P((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..dc3cf05
--- /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 __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+static void *rpcbproc_dump_3_local __P((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) __P((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..8e2ed10
--- /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 __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+static void *rpcbproc_getversaddr_4_local __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+static void *rpcbproc_getaddrlist_4_local
+ __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+static void free_rpcb_entry_list __P((rpcb_entry_list_ptr *));
+static void *rpcbproc_dump_4_local __P((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) __P((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..0a72b66
--- /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 __P((XDR *, struct encap_parms *));
+static bool_t xdr_rmtcall_args __P((XDR *, struct r_rmtcall_args *));
+static bool_t xdr_rmtcall_result __P((XDR *, struct r_rmtcall_args *));
+static bool_t xdr_opaque_parms __P((XDR *, struct r_rmtcall_args *));
+static int find_rmtcallfd_by_netid __P((char *));
+static SVCXPRT *find_rmtcallxprt_by_fd __P((int));
+static int forward_register __P((u_int32_t, struct netbuf *, int, char *,
+ rpcproc_t, rpcvers_t, u_int32_t *));
+static struct finfo *forward_find __P((u_int32_t));
+static int free_slot_by_xid __P((u_int32_t));
+static int free_slot_by_index __P((int));
+static int netbufcmp __P((struct netbuf *, struct netbuf *));
+static struct netbuf *netbufdup __P((struct netbuf *));
+static void netbuffree __P((struct netbuf *));
+static int check_rmtcalls __P((struct pollfd *, int));
+static void xprt_set_caller __P((SVCXPRT *, struct finfo *));
+static void send_svcsyserr __P((SVCXPRT *, struct finfo *));
+static void handle_reply __P((int, SVCXPRT *));
+static void find_versions __P((rpcprog_t, char *, rpcvers_t *, rpcvers_t *));
+static rpcblist_ptr find_service __P((rpcprog_t, rpcvers_t, char *));
+static char *getowner __P((SVCXPRT *, char *, size_t));
+static int add_pmaplist __P((RPCB *));
+static int del_pmaplist __P((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..c7c3bd4
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.8
@@ -0,0 +1,144 @@
+.\" @(#)rpcbind.1m 1.19 92/09/14 SMI; from SVr4
+.\" Copyright 1989 AT&T
+.\" Copyright 1991 Sun Microsystems, Inc.
+.\" $FreeBSD$
+.Dd September 14, 1992
+.Dt RPCBIND 8
+.Os
+.Sh NAME
+.Nm rpcbind
+.Nd universal addresses to RPC program number mapper
+.Sh SYNOPSIS
+.Nm
+.Op Fl adhiLls
+.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 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
+Specify specific IP addresses to bind to for 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 SEE ALSO
+.Xr rpcbind 3 ,
+.Xr rpcinfo 8
+.Sh FILES
+.Bl -tag -width /var/run/rpcbind.sock -compact
+.It Pa /var/run/rpcbind.sock
+.El
diff --git a/usr.sbin/rpcbind/rpcbind.c b/usr.sbin/rpcbind/rpcbind.c
new file mode 100644
index 0000000..9822140
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.c
@@ -0,0 +1,759 @@
+/* $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 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 __P((int, char *[]));
+
+static int init_transport __P((struct netconfig *));
+static void rbllist_add __P((rpcprog_t, rpcvers_t, struct netconfig *,
+ struct netbuf *));
+static void terminate __P((int));
+static void parseargs __P((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)
+ 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 checkbind;
+ 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. For NC_TPI_CLTS
+ * we call this later, for each socket we like to bind.
+ */
+ if (nconf->nc_semantics != NC_TPI_CLTS) {
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ syslog(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 (nconf->nc_semantics == NC_TPI_CLTS) {
+ /*
+ * 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
+ */
+ checkbind = 1;
+ 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);
+ 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)
+ 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)
+ continue;
+ }
+ if (setsockopt(fd, IPPROTO_IPV6,
+ IPV6_V6ONLY, &on, sizeof on) < 0) {
+ syslog(LOG_ERR,
+ "can't set v6-only binding for "
+ "udp6 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 ((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
+ checkbind++;
+ (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 (!checkbind)
+ return 1;
+ } else {
+ if ((strcmp(nconf->nc_netid, "local") != 0) &&
+ (strcmp(nconf->nc_netid, "unix") != 0)) {
+ if ((aicode = getaddrinfo(NULL, servname, &hints, &res))
+ != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ return 1;
+ }
+ 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: %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;
+
+ while ((c = getopt(argc, argv, "dwah:ilLs")) != -1) {
+ switch (c) {
+ 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 [-Idwils]\n");
+ exit (1);
+ }
+ }
+ if (doabort && !debugging) {
+ fprintf(stderr,
+ "-a (abort) specified without -d (debugging) -- ignored.\n");
+ doabort = 0;
+ }
+}
+
+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..2d52be3
--- /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 __P((struct netconfig *, struct netbuf *));
+bool_t is_bound __P((char *, char *));
+char *mergeaddr __P((SVCXPRT *, char *, char *, char *));
+struct netconfig *rpcbind_get_conf __P((char *));
+
+void rpcbs_init __P((void));
+void rpcbs_procinfo __P((rpcvers_t, rpcproc_t));
+void rpcbs_set __P((rpcvers_t, bool_t));
+void rpcbs_unset __P((rpcvers_t, bool_t));
+void rpcbs_getaddr __P((rpcvers_t, rpcprog_t, rpcvers_t, char *, char *));
+void rpcbs_rmtcall __P((rpcvers_t, rpcproc_t, rpcprog_t, rpcvers_t, rpcproc_t,
+ char *, rpcblist_ptr));
+void *rpcbproc_getstat __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+
+void rpcb_service_3 __P((struct svc_req *, SVCXPRT *));
+void rpcb_service_4 __P((struct svc_req *, SVCXPRT *));
+
+/* Common functions shared between versions */
+void *rpcbproc_set_com __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+void *rpcbproc_unset_com __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t));
+bool_t map_set __P((RPCB *, char *));
+bool_t map_unset __P((RPCB *, char *));
+void delete_prog __P((unsigned int));
+void *rpcbproc_getaddr_com __P((RPCB *, struct svc_req *, SVCXPRT *, rpcvers_t,
+ rpcvers_t));
+void *rpcbproc_gettime_com __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+void *rpcbproc_uaddr2taddr_com __P((void *, struct svc_req *,
+ SVCXPRT *, rpcvers_t));
+void *rpcbproc_taddr2uaddr_com __P((void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t));
+int create_rmtcall_fd __P((struct netconfig *));
+void rpcbproc_callit_com __P((struct svc_req *, SVCXPRT *, rpcvers_t,
+ rpcvers_t));
+void my_svc_run __P((void));
+
+void rpcbind_abort __P((void));
+void reap __P((int));
+void toggle_verboselog __P((int));
+
+int check_access __P((SVCXPRT *, rpcproc_t, void *, unsigned int));
+int check_callit __P((SVCXPRT *, struct r_rmtcall_args *, int));
+void logit __P((int, struct sockaddr *, rpcproc_t, rpcprog_t, const char *));
+int is_loopback __P((struct netbuf *));
+
+#ifdef PORTMAP
+extern void pmap_service __P((struct svc_req *, SVCXPRT *));
+#endif
+
+void write_warmstart __P((void));
+void read_warmstart __P((void));
+
+char *addrmerge __P((struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
+ char *netid));
+void network_init __P((void));
+struct sockaddr *local_sa __P((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..4137ce2
--- /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 __P((void *, void *, void *, int));
+#ifdef INET6
+static void in6_fillscopeid __P((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..1962e1c
--- /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 __P((char *, xdrproc_t, void *));
+static bool_t read_struct __P((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..21a9489
--- /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+= -DINET6 -DIPSEC -I. -I${.CURDIR}
+YFLAGS= -d
+
+LDADD= -lipsec -lcompat -ll -ly
+DPADD= ${LIBIPSEC} ${LIBCOMPAT} ${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..6ad9953
--- /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 __P((FILE **));
+void yyerror __P((const char *));
+int yylex __P((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 __P((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..d7c15ab
--- /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 __P((const char *s));
+extern int yylex __P((void));
+static struct payload_list * pllist_lookup __P((int seqnum));
+static void pllist_enqueue __P((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..3ad9a3c
--- /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 RETURN VALUES
+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..3d08fa8
--- /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 <netinet6/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 __P((FILE **));
+
+static void show_usage __P((void));
+static void init_sin6 __P((struct sockaddr_in6 *, const char *));
+#if 0
+static void join_multi __P((const char *));
+#endif
+static void init_globals __P((void));
+static void config __P((FILE **));
+#ifdef IPSEC_POLICY_IPSEC
+static void sock6_open __P((struct flags *, char *));
+static void sock4_open __P((struct flags *, char *));
+#else
+static void sock6_open __P((struct flags *));
+static void sock4_open __P((struct flags *));
+#endif
+static void rrenum_output __P((struct payload_list *, struct dst_list *));
+static void rrenum_snd_eachdst __P((struct payload_list *));
+#if 0
+static void rrenum_snd_fullsequence __P((void));
+#endif
+static void rrenum_input __P((int));
+int main __P((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?)",
+ __FUNCTION__);
+ 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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", __FUNCTION__);
+ 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", __FUNCTION__);
+ 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", __FUNCTION__);
+ 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", __FUNCTION__,
+ 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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", __FUNCTION__,
+ 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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", __FUNCTION__,
+ 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", __FUNCTION__,
+ 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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",
+ __FUNCTION__, 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..fde2a92
--- /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 don't support IPv6 multicast forwarding,
+you'll 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..d1ca23b
--- /dev/null
+++ b/usr.sbin/rtadvd/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= rtadvd
+MAN= rtadvd.conf.5 rtadvd.8
+SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c
+
+CFLAGS+= -DINET6 -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO
+
+DPADD= ${LIBCOMPAT}
+LDADD= -lcompat
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c
new file mode 100644
index 0000000..b83fbbb
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.c
@@ -0,0 +1,455 @@
+/* $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.
+ * 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.
+ */
+
+/*
+ * 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 __P((char *, char *));
+int getent __P((char *, char *, char *));
+int tnchktc __P((void));
+int tnamatch __P((char *));
+static char *tskip __P((char *));
+int64_t tgetnum __P((char *));
+int tgetflag __P((char *));
+char *tgetstr __P((char *, char **));
+static char *tdecode __P((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..3723871
--- /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 __P((char *, const char *));
+extern int agetflag __P((const char *));
+extern int64_t agetnum __P((const char *));
+extern char *agetstr __P((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..27bedd7
--- /dev/null
+++ b/usr.sbin/rtadvd/config.c
@@ -0,0 +1,1049 @@
+/* $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 <arpa/inet.h>
+
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.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 __P((void *));
+static void makeentry __P((char *, size_t, int, char *));
+static int getinet6sysctl __P((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);
+ }
+
+ /* 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++ &= *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) shoule 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..aeb1c2c
--- /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 __P((char *));
+extern void delete_prefix __P((struct prefix *));
+extern void invalidate_prefix __P((struct prefix *));
+extern void update_prefix __P((struct prefix *));
+extern void make_prefix __P((struct rainfo *, int, struct in6_addr *, int));
+extern void make_packet __P((struct rainfo *));
+extern void get_prefix __P((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..7b9a274
--- /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 __P((struct sockaddr_dl *));
+static void if_dump __P((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..cc3b472
--- /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 __P((char *));
diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c
new file mode 100644
index 0000000..07d9e1f
--- /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 __P((char **buf, size_t *size));
+static void parse_iflist __P((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..07bb6ee
--- /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 __P((char *));
+int if_getmtu __P((char *));
+int if_getflags __P((int, int));
+int lladdropt_length __P((struct sockaddr_dl *));
+void lladdropt_fill __P((struct sockaddr_dl *, struct nd_opt_hdr *));
+int rtbuf_len __P((void));
+char *get_next_msg __P((char *, char *, int, size_t *, int));
+struct in6_addr *get_addr __P((char *));
+int get_rtm_ifindex __P((char *));
+int get_ifm_ifindex __P((char *));
+int get_ifam_ifindex __P((char *));
+int get_ifm_flags __P((char *));
+int get_prefixlen __P((char *));
+int prefixlen __P((u_char *, u_char *));
+int rtmsg_type __P((char *));
+int ifmsg_type __P((char *));
+int rtmsg_len __P((char *));
+int ifmsg_len __P((char *));
+void init_iflist __P((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..14a8ab9
--- /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..ce94015
--- /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 __P((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..6fa41d6
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.8
@@ -0,0 +1,196 @@
+.\" $FreeBSD$
+.\" $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.
+.\"
+.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 RETURN VALUES
+The
+.Nm
+program exits 0 on success, and >0 on failures.
+.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..8a5092e
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.c
@@ -0,0 +1,1690 @@
+/* $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 <stdlib.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 __P((int, char *[]));
+static void set_die __P((int));
+static void die __P((void));
+static void sock_open __P((void));
+static void rtsock_open __P((void));
+static void rtadvd_input __P((void));
+static void rs_input __P((int, struct nd_router_solicit *,
+ struct in6_pktinfo *, struct sockaddr_in6 *));
+static void ra_input __P((int, struct nd_router_advert *,
+ struct in6_pktinfo *, struct sockaddr_in6 *));
+static int prefix_check __P((struct nd_opt_prefix_info *, struct rainfo *,
+ struct sockaddr_in6 *));
+static int nd6_options __P((struct nd_opt_hdr *, int,
+ union nd_opts *, u_int32_t));
+static void free_ndopts __P((union nd_opts *));
+static void ra_output __P((struct rainfo *));
+static void rtmsg_input __P((void));
+static void rtadvd_set_dump_file __P((int));
+static void set_short_delay __P((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_DEBUG,
+ "<%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_ERR,
+ "<%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() % 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_ERR,
+ "<%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_SOURCE_LINKADDR:
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_REDIRECTED_HEADER:
+ break; /* we don't care about these options */
+ 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() % (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..6bc98c9
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf.5
@@ -0,0 +1,426 @@
+.\" $FreeBSD$
+.\" $KAME: rtadvd.conf.5,v 1.49 2003/07/24 21:51:26 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.
+.\"
+.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 decrements
+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 decrements
+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
+.Pp
+Thomas Narten, Erik Nordmark and W. A. Simpson,
+.Do
+Neighbor Discovery for IP version 6 (IPv6)
+.Dc ,
+RFC 2461
+.Pp
+Richard Draves,
+.Do
+Default Router Preferences and More-Specific Routes
+.Dc ,
+draft-ietf-ipngwg-router-selection-xx.txt
+.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..b1c505d
--- /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 __P((void *));
+void ra_timer_update __P((void *, struct timeval *));
+
+int prefix_match __P((struct in6_addr *, int, struct in6_addr *, int));
+struct rainfo *if_indextorainfo __P((int));
+struct prefix *find_prefix __P((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..fb9f299
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.c
@@ -0,0 +1,208 @@
+/* $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 "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) __P((void *)),
+ void (*update) __P((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..d435bde
--- /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) __P((void *)); /* expiration function */
+ void *expire_data;
+ void (*update) __P((void *, struct timeval *)); /* update function */
+ void *update_data;
+};
+
+void rtadvd_timer_init __P((void));
+struct rtadvd_timer *rtadvd_add_timer __P((struct rtadvd_timer *(*) __P((void *)),
+ void (*) __P((void *, struct timeval *)), void *, void *));
+void rtadvd_set_timer __P((struct timeval *, struct rtadvd_timer *));
+void rtadvd_remove_timer __P((struct rtadvd_timer **));
+struct timeval * rtadvd_check_timer __P((void));
+struct timeval * rtadvd_timer_rest __P((struct rtadvd_timer *));
+void TIMEVAL_ADD __P((struct timeval *, struct timeval *,
+ struct timeval *));
+void TIMEVAL_SUB __P((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..6d4d260
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.1
@@ -0,0 +1,210 @@
+.\"
+.\" 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 DIAGNOSTICS
+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:
+.Bd -literal -offset indent -compact
+.Sy "rtprio"
+.Ed
+.Pp
+To see which realtime priority of process
+.Em 1423 :
+.Bd -literal -offset indent -compact
+.Sy "rtprio 1423"
+.Ed
+.Pp
+To run
+.Xr cron 8
+at the lowest realtime priority:
+.Bd -literal -offset indent -compact
+.Sy "rtprio 31 cron"
+.Ed
+.Pp
+To change the realtime priority of process
+.Em 1423
+to
+.Em 16 :
+.Bd -literal -offset indent -compact
+.Sy "rtprio 16 -1423"
+.Ed
+.Pp
+To run
+.Xr tcpdump 1
+without realtime priority:
+.Bd -literal -offset indent -compact
+.Sy "rtprio -t tcpdump"
+.Ed
+.Pp
+To change the realtime priority of process
+.Em 1423
+to
+.Dv RTP_PRIO_NORMAL
+(non-realtime/normal priority):
+.Bd -literal -offset indent -compact
+.Sy "rtprio -t -1423"
+.Ed
+.Pp
+To make depend while not disturbing other machine usage:
+.Bd -literal -offset indent -compact
+.Sy "idprio 31 make depend"
+.Ed
+.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 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.
+.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 .
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..6ae47e9
--- /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+= -DINET6 -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..bdc8ec3
--- /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 __P((void));
+static char *sec2str __P((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..23e4e6f
--- /dev/null
+++ b/usr.sbin/rtsold/if.c
@@ -0,0 +1,383 @@
+/* $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 __P((const char *));
+static void get_rtaddrs __P((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)
+ 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..b1cbc79
--- /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 __P((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..09d6774
--- /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 __P((int, struct rt_msghdr *, char *));
+#endif
+
+static struct {
+ u_char type;
+ size_t minlen;
+ int (*func) __P((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..dd9f4a1
--- /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 __P((char *, char *));
+static int safefile __P((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..1e2a3bf
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.8
@@ -0,0 +1,272 @@
+.\" $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
+.Fl f
+prevents
+.Nm
+from becoming a daemon (foreground mode).
+Warning messages are generated to standard error
+instead of
+.Xr syslog 3 .
+.It Fl F
+Configure
+.Xr sysctl 8
+variable related to
+.Nm
+by itself.
+Without
+.Fl F ,
+.Nm
+will not alter and obey the current
+.Xr sysctl 8
+settings.
+.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 RETURN VALUES
+The
+.Nm
+program exits 0 on success, and >0 on failures.
+.\"
+.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..f9dca79
--- /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 constatns */
+#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 __P((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 __P((char *));
+#endif
+static int make_packet __P((struct ifinfo *));
+static struct timeval *rtsol_check_timer __P((void));
+
+#ifndef SMALL
+static void rtsold_set_dump_file __P((int));
+#endif
+static void usage __P((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 initilizatoin 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() % (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 solicatation. 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..e25ed95
--- /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 __P((char *));
+extern void iflist_init __P((void));
+struct ifinfo *find_ifinfo __P((int));
+void rtsol_timer_update __P((struct ifinfo *));
+extern void warnmsg __P((int, const char *, const char *, ...))
+ __attribute__((__format__(__printf__, 3, 4)));
+extern char **autoifprobe __P((void));
+
+/* if.c */
+extern int ifinit __P((void));
+extern int interface_up __P((char *));
+extern int interface_status __P((struct ifinfo *));
+extern int lladdropt_length __P((struct sockaddr_dl *));
+extern void lladdropt_fill __P((struct sockaddr_dl *, struct nd_opt_hdr *));
+extern struct sockaddr_dl *if_nametosdl __P((char *));
+extern int getinet6sysctl __P((int));
+extern int setinet6sysctl __P((int, int));
+
+/* rtsol.c */
+extern int sockopen __P((void));
+extern void sendpacket __P((struct ifinfo *));
+extern void rtsol_input __P((int));
+
+/* probe.c */
+extern int probe_init __P((void));
+extern void defrouter_probe __P((struct ifinfo *));
+
+/* dump.c */
+extern void rtsold_dump_file __P((char *));
+
+/* rtsock.c */
+extern int rtsock_open __P((void));
+extern int rtsock_input __P((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..e072697
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.8
@@ -0,0 +1,232 @@
+.\" 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.
+.\"
+.\" @(#)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
+won't 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 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.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c
new file mode 100644
index 0000000..ab06d97
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.c
@@ -0,0 +1,743 @@
+/*
+ * 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[] = "@(#)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;
+ int cc, whod, len = sizeof(from);
+ 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);
+ if (utmp)
+ utmp = (struct utmp *)realloc(utmp, utmpsize);
+ else
+ utmp = (struct utmp *)malloc(utmpsize);
+ if (! utmp) {
+ 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..2f9537a
--- /dev/null
+++ b/usr.sbin/sa/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= sa
+MAN= sa.8
+SRCS= main.c pdb.c usrdb.c
+
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sa/extern.h b/usr.sbin/sa/extern.h
new file mode 100644
index 0000000..bc5f199
--- /dev/null
+++ b/usr.sbin/sa/extern.h
@@ -0,0 +1,97 @@
+/*
+ * 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 */
+
+struct cmdinfo {
+ char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */
+ u_long 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 */
+};
+#define CI_UNPRINTABLE 0x0001 /* unprintable chars in name */
+
+struct userinfo {
+ u_long 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 */
+};
+
+/* typedefs */
+
+typedef int (*cmpf_t)(const DBT *, const DBT *);
+
+/* 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 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;
+
+/* 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..fe7de23
--- /dev/null
+++ b/usr.sbin/sa/main.c
@@ -0,0 +1,554 @@
+/*
+ * 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 <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 int acct_load(char *, int);
+static u_quad_t decode_comp_t(comp_t);
+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;
+
+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)
+{
+ char pathacct[] = _PATH_ACCT;
+ int ch, error = 0;
+
+ dfltargv[0] = pathacct;
+
+ while ((ch = getopt(argc, argv, "abcdDfijkKlmnqrstuv:")) != -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 '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 '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++) {
+ int fd;
+
+ /*
+ * load the accounting data from the file.
+ * if it fails, go on to the next file.
+ */
+ fd = acct_load(argv[0], sflag);
+ if (fd < 0)
+ 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(fd, 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 (close(fd) == -1) {
+ warn("close %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] [-v cutoff] [file ...]\n");
+ exit(1);
+}
+
+static int
+acct_load(pn, wr)
+ char *pn;
+ int wr;
+{
+ struct acct ac;
+ struct cmdinfo ci;
+ ssize_t rv;
+ int fd, i;
+
+ /*
+ * open the file
+ */
+ fd = open(pn, wr ? O_RDWR : O_RDONLY, 0);
+ if (fd == -1) {
+ warn("open %s %s", pn, wr ? "for read/write" : "read-only");
+ return (-1);
+ }
+
+ /*
+ * 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 = read(fd, &ac, sizeof(struct acct));
+ if (rv == -1)
+ warn("error reading %s", pn);
+ else if (rv > 0 && rv < (int)sizeof(struct acct))
+ warnx("short read of accounting data in %s", pn);
+ if (rv != sizeof(struct acct))
+ 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_flag & AFORK)
+ ci.ci_comm[i++] = '*';
+ ci.ci_comm[i++] = '\0';
+ ci.ci_etime = decode_comp_t(ac.ac_etime);
+ ci.ci_utime = decode_comp_t(ac.ac_utime);
+ ci.ci_stime = decode_comp_t(ac.ac_stime);
+ ci.ci_uid = ac.ac_uid;
+ ci.ci_mem = ac.ac_mem;
+ ci.ci_io = decode_comp_t(ac.ac_io) / AHZ;
+
+ 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("%6lu %12.2f cpu %12juk mem %12ju io %s\n",
+ ci.ci_uid,
+ (ci.ci_utime + ci.ci_stime) / (double) AHZ,
+ (uintmax_t)ci.ci_mem, (uintmax_t)ci.ci_io,
+ ci.ci_comm);
+ }
+
+ /* finally, return the file descriptor for possible truncation */
+ return (fd);
+}
+
+static u_quad_t
+decode_comp_t(comp)
+ comp_t comp;
+{
+ u_quad_t rv;
+
+ /*
+ * for more info on the comp_t format, see:
+ * /usr/src/sys/kern/kern_acct.c
+ * /usr/src/sys/sys/acct.h
+ * /usr/src/usr.bin/lastcomm/lastcomm.c
+ */
+ rv = comp & 0x1fff; /* 13 bit fraction */
+ comp >>= 13; /* 3 bit base-8 exponent */
+ while (comp--)
+ rv <<= 3;
+
+ return (rv);
+}
+
+/* sort commands, doing the right thing in terms of reversals */
+static int
+cmp_comm(s1, s2)
+ const char *s1, *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(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+ u_quad_t 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(d1, d2)
+ const DBT *d1, *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(d1, d2)
+ const DBT *d1, *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(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+ double n1, n2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ n1 = (double) c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1);
+ n2 = (double) 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(d1, d2)
+ const DBT *d1, *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(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo c1, c2;
+ u_quad_t 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 = (double) c1.ci_mem / (double) (t1 ? t1 : 1);
+ n2 = (double) c2.ci_mem / (double) (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(d1, d2)
+ const DBT *d1, *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..e82f973
--- /dev/null
+++ b/usr.sbin/sa/pdb.c
@@ -0,0 +1,422 @@
+/*
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int check_junk __P((struct cmdinfo *));
+static void add_ci __P((const struct cmdinfo *, struct cmdinfo *));
+static void print_ci __P((const struct cmdinfo *, const struct cmdinfo *));
+
+static DB *pacct_db;
+
+int
+pacct_init()
+{
+ DB *saved_pacct_db;
+ int error;
+
+ pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL);
+ if (pacct_db == NULL)
+ return (-1);
+
+ error = 0;
+ if (!iflag) {
+ DBT key, data;
+ int serr, nerr;
+
+ saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDONLY, 0, DB_BTREE,
+ NULL);
+ if (saved_pacct_db == NULL) {
+ error = errno == ENOENT ? 0 : -1;
+ if (error)
+ warn("retrieving process accounting summary");
+ goto out;
+ }
+
+ serr = DB_SEQ(saved_pacct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving process accounting summary");
+ error = -1;
+ goto closeout;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(pacct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("initializing process accounting stats");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(saved_pacct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving process accounting summary");
+ error = -1;
+ break;
+ }
+ }
+
+closeout: if (DB_CLOSE(saved_pacct_db) < 0) {
+ warn("closing process accounting summary");
+ error = -1;
+ }
+ }
+
+out: if (error != 0)
+ pacct_destroy();
+ return (error);
+}
+
+void
+pacct_destroy()
+{
+ if (DB_CLOSE(pacct_db) < 0)
+ warn("destroying process accounting stats");
+}
+
+int
+pacct_add(ci)
+ 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);
+}
+
+int
+pacct_update()
+{
+ DB *saved_pacct_db;
+ DBT key, data;
+ int error, serr, nerr;
+
+ saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
+ DB_BTREE, NULL);
+ if (saved_pacct_db == NULL) {
+ warn("creating process accounting summary");
+ return (-1);
+ }
+
+ error = 0;
+
+ serr = DB_SEQ(pacct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving process accounting stats");
+ error = -1;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(saved_pacct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("saving process accounting summary");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(pacct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving process accounting stats");
+ error = -1;
+ break;
+ }
+ }
+
+ if (DB_SYNC(saved_pacct_db, 0) < 0) {
+ warn("syncing process accounting summary");
+ error = -1;
+ }
+ if (DB_CLOSE(saved_pacct_db) < 0) {
+ warn("closing process accounting summary");
+ error = -1;
+ }
+ return error;
+}
+
+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(cip)
+ 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(fromcip, tocip)
+ 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(cip, totalcip)
+ const struct cmdinfo *cip, *totalcip;
+{
+ double t, c;
+ int uflow;
+
+ c = cip->ci_calls ? cip->ci_calls : 1;
+ t = (cip->ci_utime + cip->ci_stime) / (double) AHZ;
+ 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.2f%% ",
+ cip->ci_calls / (double) totalcip->ci_calls);
+ else
+ printf(" %4s ", "");
+ }
+
+ if (jflag)
+ printf("%11.2fre ", cip->ci_etime / (double) (AHZ * c));
+ else
+ printf("%11.2fre ", cip->ci_etime / (60.0 * AHZ));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ",
+ cip->ci_etime / (double) totalcip->ci_etime);
+ else
+ printf(" %4s ", "");
+ }
+
+ if (!lflag) {
+ if (jflag)
+ printf("%11.2fcp ", t / (double) cip->ci_calls);
+ else
+ printf("%11.2fcp ", t / 60.0);
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ",
+ (cip->ci_utime + cip->ci_stime) / (double)
+ (totalcip->ci_utime + totalcip->ci_stime));
+ else
+ printf(" %4s ", "");
+ }
+ } else {
+ if (jflag)
+ printf("%11.2fu ", cip->ci_utime / (double) (AHZ * c));
+ else
+ printf("%11.2fu ", cip->ci_utime / (60.0 * AHZ));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ", cip->ci_utime / (double) totalcip->ci_utime);
+ else
+ printf(" %4s ", "");
+ }
+ if (jflag)
+ printf("%11.2fs ", cip->ci_stime / (double) (AHZ * c));
+ else
+ printf("%11.2fs ", cip->ci_stime / (60.0 * AHZ));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ", cip->ci_stime / (double) totalcip->ci_stime);
+ else
+ printf(" %4s ", "");
+ }
+ }
+
+ if (tflag) {
+ if (!uflow)
+ printf("%8.2fre/cp ",
+ cip->ci_etime /
+ (double) (cip->ci_utime + cip->ci_stime));
+ else
+ printf("*ignore* ");
+ }
+
+ if (Dflag)
+ printf("%10jutio ", (uintmax_t)cip->ci_io);
+ else
+ printf("%8.0favio ", cip->ci_io / c);
+
+ if (Kflag)
+ printf("%10juk*sec ", (uintmax_t)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..794626e
--- /dev/null
+++ b/usr.sbin/sa/sa.8
@@ -0,0 +1,237 @@
+.\"
+.\" 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 February 25, 1994
+.Dt SA 8
+.Os
+.Sh NAME
+.Nm sa
+.Nd print system accounting statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl abcdDfijkKlmnqrstu
+.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 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
+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 DIAGNOSTICS
+.Ex -std
+.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 SEE ALSO
+.Xr lastcomm 1 ,
+.Xr acct 5 ,
+.Xr ac 8 ,
+.Xr accton 8
+.Sh BUGS
+The number of options to this program is absurd, especially considering
+that there's 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.
+.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
diff --git a/usr.sbin/sa/usrdb.c b/usr.sbin/sa/usrdb.c
new file mode 100644
index 0000000..d2f739d
--- /dev/null
+++ b/usr.sbin/sa/usrdb.c
@@ -0,0 +1,286 @@
+/*
+ * 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 __P((const DBT *, const DBT *));
+
+static DB *usracct_db;
+
+int
+usracct_init()
+{
+ DB *saved_usracct_db;
+ BTREEINFO bti;
+ int error;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ usracct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
+ if (usracct_db == NULL)
+ return (-1);
+
+ error = 0;
+ if (!iflag) {
+ DBT key, data;
+ int serr, nerr;
+
+ saved_usracct_db = dbopen(_PATH_USRACCT, O_RDONLY, 0, DB_BTREE,
+ &bti);
+ if (saved_usracct_db == NULL) {
+ error = (errno == ENOENT) ? 0 : -1;
+ if (error)
+ warn("retrieving user accounting summary");
+ goto out;
+ }
+
+ serr = DB_SEQ(saved_usracct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving user accounting summary");
+ error = -1;
+ goto closeout;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(usracct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("initializing user accounting stats");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(saved_usracct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving user accounting summary");
+ error = -1;
+ break;
+ }
+ }
+
+closeout:
+ if (DB_CLOSE(saved_usracct_db) < 0) {
+ warn("closing user accounting summary");
+ error = -1;
+ }
+ }
+
+out:
+ if (error != 0)
+ usracct_destroy();
+ return (error);
+}
+
+void
+usracct_destroy()
+{
+ if (DB_CLOSE(usracct_db) < 0)
+ warn("destroying user accounting stats");
+}
+
+int
+usracct_add(ci)
+ const struct cmdinfo *ci;
+{
+ DBT key, data;
+ struct userinfo newui;
+ u_long 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 %lu 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 %lu != expected record number %lu",
+ 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 %lu to user accounting stats", uid);
+ return (-1);
+ } else if (rv != 0) {
+ warnx("DB_PUT returned 1");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+usracct_update()
+{
+ DB *saved_usracct_db;
+ DBT key, data;
+ BTREEINFO bti;
+ int error, serr, nerr;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ saved_usracct_db = dbopen(_PATH_USRACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
+ DB_BTREE, &bti);
+ if (saved_usracct_db == NULL) {
+ warn("creating user accounting summary");
+ return (-1);
+ }
+
+ error = 0;
+
+ serr = DB_SEQ(usracct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving user accounting stats");
+ error = -1;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(saved_usracct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("saving user accounting summary");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(usracct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving user accounting stats");
+ error = -1;
+ break;
+ }
+ }
+
+ if (DB_SYNC(saved_usracct_db, 0) < 0) {
+ warn("syncing process accounting summary");
+ error = -1;
+ }
+ if (DB_CLOSE(saved_usracct_db) < 0) {
+ warn("closing process accounting summary");
+ error = -1;
+ }
+ return error;
+}
+
+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 = (double) (ui->ui_utime + ui->ui_stime) /
+ (double) AHZ;
+ if (t < 0.0001) /* kill divide by zero */
+ t = 0.0001;
+
+ printf("%12.2f%s ", t / 60.0, "cpu");
+
+ /* ui->ui_calls is always != 0 */
+ if (dflag)
+ printf("%12ju%s",
+ (uintmax_t)(ui->ui_io / ui->ui_calls), "avio");
+ else
+ printf("%12ju%s", (uintmax_t)ui->ui_io, "tio");
+
+ /* t is always >= 0.0001; see above */
+ if (kflag)
+ printf("%12.0f%s", ui->ui_mem / t, "k");
+ else
+ printf("%12ju%s", (uintmax_t)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(k1, k2)
+ const DBT *k1, *k2;
+{
+ u_long 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..e11e73e
--- /dev/null
+++ b/usr.sbin/sade/Makefile
@@ -0,0 +1,98 @@
+# $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 tape.c tcpip.c termcap.c ttys.c ufs.c usb.c user.c \
+ variable.c ${_wizard} keymap.h
+
+.if ${MACHINE} == "pc98"
+SRCS+= pccard.c
+.endif
+
+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
+
+CLEANFILES= makedevs.c rtermcap
+CLEANFILES+= keymap.tmp keymap.h
+
+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 \
+ cs.latin2.qwertz danish.iso el.iso07 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 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 " { 0 }"; echo "};" ; echo "" ) >> keymap.tmp
+ mv keymap.tmp keymap.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sade/command.c b/usr.sbin/sade/command.c
new file mode 100644
index 0000000..33ebc35
--- /dev/null
+++ b/usr.sbin/sade/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/sade/config.c b/usr.sbin/sade/config.c
new file mode 100644
index 0000000..d055933
--- /dev/null
+++ b/usr.sbin/sade/config.c
@@ -0,0 +1,1253 @@
+/*
+ * 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 <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 sysinstall then they may NOT\n"
+ "be found by this run!");
+ }
+ }
+
+ 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) {
+ if (c1->type == freebsd) {
+ 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.
+ */
+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
+ fprintf(rcSite, "#REMOVED: %s", line);
+ }
+ fclose(rcOld);
+ } else {
+ 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
+configUsers(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+#ifdef WITH_LINUX
+int
+configLinux(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+ int i;
+
+ dialog_clear_norefresh();
+ variable_set2(VAR_LINUX_ENABLE, "YES", 1);
+ Mkdir("/compat/linux");
+ msgNotify("Installing Linux compatibility library...");
+ i = package_add("linux_base");
+ restorescr(w);
+ return i;
+}
+#endif
+
+#ifdef __alpha__
+int
+configOSF1(dialogMenuItem *self)
+{
+
+ variable_set2(VAR_OSF1_ENABLE, "YES", 1);
+ Mkdir("/compat/osf1");
+ return DITEM_SUCCESS;
+}
+#endif
+
+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
+configSecurity(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuSecurity, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static void
+write_root_xprofile(char *str)
+{
+ FILE *fp;
+ int len;
+ char **cp;
+ static char *flist[] = { /* take care of both xdm and startx */
+ "/root/.xinitrc",
+ "/root/.xsession",
+ "/usr/share/skel/dot.xinitrc",
+ "/usr/share/skel/dot.xsession",
+ NULL,
+ };
+
+ len = strlen(str);
+ for (cp = flist; *cp; cp++) {
+ fp = fopen(*cp, "w");
+ if (fp) {
+ fwrite(str, 1, len, fp);
+ fchmod(fileno(fp), 0755);
+ fclose(fp);
+ }
+ }
+}
+
+static int
+gotit(char *fname)
+{
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, sizeof tmp, "/usr/X11R6/bin/%s", fname);
+ if (file_executable(tmp))
+ return TRUE;
+ snprintf(tmp, sizeof tmp, "/usr/local/bin/%s", fname);
+ return file_executable(tmp);
+}
+
+int
+configXDesktop(dialogMenuItem *self)
+{
+ char *desk;
+ int ret = DITEM_SUCCESS;
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXDesktops, FALSE) || !(desk = variable_get(VAR_DESKSTYLE))) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ if (!strcmp(desk, "kde")) {
+ ret = package_add("kde-lite");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("startkde"))
+ write_root_xprofile("exec startkde\n");
+ }
+ else if (!strcmp(desk, "gnome2")) {
+ ret = package_add("gnome2-lite");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session"))
+ write_root_xprofile("exec gnome-session\n");
+ }
+ else if (!strcmp(desk, "afterstep")) {
+ ret = package_add("afterstep");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("afterstep"))
+ write_root_xprofile("exec afterstep\n");
+ }
+ else if (!strcmp(desk, "windowmaker")) {
+ ret = package_add("windowmaker");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("wmaker.inst")) {
+ write_root_xprofile("xterm &\n[ ! -d $HOME/GNUstep/Library/WindowMaker ] && /usr/X11R6/bin/wmaker.inst\nexec /usr/X11R6/bin/wmaker\n");
+ }
+ }
+ else if (!strcmp(desk, "fvwm2")) {
+ ret = package_add("fvwm");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("fvwm"))
+ write_root_xprofile("exec fvwm\n");
+ }
+ if (DITEM_STATUS(ret) == DITEM_FAILURE)
+ msgConfirm("An error occurred while adding the package(s) required\n"
+ "by this desktop type. Please change installation media\n"
+ "and/or select a different, perhaps simpler, desktop\n"
+ "environment and try again.");
+ restorescr(w);
+ return ret;
+}
+
+int
+configXSetup(dialogMenuItem *self)
+{
+ char *config, *execfile, *execcmd, *style, *tmp;
+#ifdef WITH_MICE
+ char *moused;
+#endif
+ WINDOW *w = savescr();
+
+ setenv("XWINHOME", "/usr/X11R6", 1);
+tryagain:
+ variable_unset(VAR_DESKSTYLE);
+ variable_unset(VAR_XF86_CONFIG);
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE)) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ config = variable_get(VAR_XF86_CONFIG);
+ style = variable_get(VAR_DESKSTYLE);
+ if (!config) {
+ if (style)
+ goto config_desktop;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+
+ if (file_readable("/var/run/ld-elf.so.hints"))
+ vsystem("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ else
+ vsystem("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+
+ vsystem("/sbin/ifconfig lo0 127.0.0.1");
+
+ /*
+ * execcmd may have been passed in as a command name with
+ * arguments. Therefore, before determining if it is suitable for
+ * execution, we must split off the filename component from the
+ * command line arguments.
+ */
+
+ execcmd = string_concat("/usr/X11R6/bin/", config);
+ execfile = strdup(execcmd);
+ if ((tmp = strchr(execfile, ' ')))
+ *tmp = '\0';
+ if (file_executable(execfile)) {
+ free(execfile);
+#ifdef WITH_MICE
+ moused = variable_get(VAR_MOUSED);
+ while (!moused || strcmp(moused, "YES")) {
+ if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
+ "or indirect access via the mouse daemon. You have not\n"
+ "configured the mouse daemon. Would you like to configure it\n"
+ "now? If you intend to let the X server access the mouse\n"
+ "directly, choose \"No\" at this time."))
+ break;
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ moused = variable_get(VAR_MOUSED);
+ }
+ if (moused && !strcmp(moused, "YES"))
+ msgConfirm("You have configured and are now running the mouse daemon.\n"
+ "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
+ "\"MouseSystems\" as the mouse protocol in the X configuration\n"
+ "utility.");
+#endif
+ Mkdir("/etc/X11"); /* XXX:Remove this later after we are happy mtree will have created this for us. */
+ systemExecute(execcmd);
+ if (!file_readable("/etc/X11/XF86Config")) {
+ if (!msgYesNo("The XFree86 configuration process seems to have\nfailed. Would you like to try again?"))
+ goto tryagain;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+config_desktop:
+ configXDesktop(self);
+ restorescr(w);
+ return DITEM_SUCCESS;
+ }
+ else {
+ free(execfile);
+ msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
+ "Please install this before attempting to configure XFree86.");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+}
+
+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.%s localhost\n", dp);
+ fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\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;
+
+ /* 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;
+ for (tmp = Plist.kids; tmp && tmp->name; tmp = tmp->next)
+ (void)index_extract(mediaDevice, &Top, tmp, FALSE);
+ 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/ports 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 synatx 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)
+{
+ int retval = 0;
+ int doupdate = 1;
+
+ if (self != NULL) {
+ retval = dmenuToggleVariable(self);
+ if (strcmp(variable_get(self->data), "YES") != 0)
+ doupdate = 0;
+ }
+
+ 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");
+ 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/sade/devices.c b/usr.sbin/sade/devices.c
new file mode 100644
index 0000000..e638201
--- /dev/null
+++ b/usr.sbin/sade/devices.c
@@ -0,0 +1,582 @@
+/*
+ * 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>
+
+/* 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;
+
+static struct _devname {
+ DeviceType type;
+ char *name;
+ char *description;
+ int major, minor, delta, max;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd%d", "SCSI CDROM drive", 15, 2, 8, 4 },
+ { DEVICE_TYPE_CDROM, "mcd%d", "Mitsumi (old model) CDROM drive", 29, 0, 8, 4 },
+ { DEVICE_TYPE_CDROM, "scd%d", "Sony CDROM drive - CDU31/33A type", 45, 0, 8, 4 },
+#ifdef notdef
+ { DEVICE_TYPE_CDROM, "matcd%d", "Matsushita CDROM ('sound blaster' type)", 46, 0, 8, 4 },
+#endif
+ { DEVICE_TYPE_CDROM, "acd%d", "ATAPI/IDE CDROM", 117, 0, 8, 4 },
+ { DEVICE_TYPE_TAPE, "rsa%d", "SCSI tape drive", 14, 0, 16, 4 },
+ { DEVICE_TYPE_TAPE, "rwt%d", "Wangtek tape drive", 10, 0, 1, 4 },
+ { DEVICE_TYPE_DISK, "da%d", "SCSI disk device", 13, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ad%d", "ATA/IDE disk device", 116, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ar%d", "ATA/IDE RAID device", 157, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "fla%d", "M-Systems DiskOnChip Flash devicee", 102, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "afd%d", "ATAPI/IDE floppy device", 118, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "mlxd%d", "Mylex RAID disk", 131, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "amrd%d", "AMI MegaRAID drive", 133, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "idad%d", "Compaq RAID array", 109, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "twed%d", "3ware ATA RAID array", 147, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "aacd%d", "Adaptec FSA RAID array", 151, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "ipsd%d", "IBM ServeRAID RAID array", 176, 65538, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 9, 0, 64, 4 },
+ { DEVICE_TYPE_NETWORK, "an", "Aironet 4500/4800 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "aue", "ADMtek USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "axe", "ASIX Electronics USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "bfe", "Broadcom BCM440x PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "bge", "Broadcom BCM570x PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cue", "CATC USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" },
+ { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" },
+ { DEVICE_TYPE_NETWORK, "dc", "DEC/Intel 21143 (and clones) PCI fast ethernet card" },
+ { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" },
+ { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "em", "Intel(R) PRO/1000 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ex", "Intel EtherExpress Pro/10 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "fe", "Fujitsu MB86960A/MB86965A ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210" },
+ { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" },
+ { DEVICE_TYPE_NETWORK, "kue", "Kawasaki LSI USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "lge", "Level 1 LXT1001 gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "nge", "NatSemi PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "pcn", "AMD Am79c79x PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ray", "Raytheon Raylink 802.11 wireless adaptor" },
+ { DEVICE_TYPE_NETWORK, "re", "RealTek 8139C+/8169/8169S/8110S PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "rue", "RealTek USB ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" },
+#ifdef PC98
+ { DEVICE_TYPE_NETWORK, "snc", "SONIC ethernet card" },
+#endif
+ { DEVICE_TYPE_NETWORK, "sn", "SMC/Megahertz ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ste", "Sundance ST201 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sk", "SysKonnect PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX ethernet card" },
+ { DEVICE_TYPE_NETWORK, "txp", "3Com 3cR990 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ti", "Alteon Networks PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tl", "Texas Instruments ThunderLAN PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vr", "VIA VT3043/VT86C100A Rhine PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vlan", "IEEE 802.1Q VLAN network interface" },
+ { DEVICE_TYPE_NETWORK, "vx", "3COM 3c590 / 3c595 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wb", "Winbond W89C840F PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wi", "Lucent WaveLAN/IEEE 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "wx", "Intel Gigabit Ethernet (82452) card" },
+ { DEVICE_TYPE_NETWORK, "xe", "Xircom/Intel EtherExpress Pro100/16 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "xl", "3COM 3c90x / 3c90xB PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cuaa%d", "%s on device %s (COM%d)", 28, 128, 1, 16 },
+ { DEVICE_TYPE_NETWORK, "fwe", "FireWire Ethernet emulation" },
+ { DEVICE_TYPE_NETWORK, "lp", "Parallel Port IP (PLIP) peer connection" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+ { DEVICE_TYPE_NETWORK, "disc", "Software discard network interface" },
+#ifdef PC98
+ { DEVICE_TYPE_DISK, "wd%d", "IDE disk device", 3, 65538, 8, 16 },
+ { DEVICE_TYPE_CDROM, "wcd%dc", "ATAPI IDE CDROM", 69, 2, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 87, 0, 8, 4 },
+ { DEVICE_TYPE_DISK, "wfd%d", "ATAPI floppy device", 87, 65538, 8, 4 },
+#endif
+ { 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];
+ mode_t m;
+ dev_t d;
+ int fail;
+
+ 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);
+ return fd;
+ }
+ m = 0640 | S_IFCHR;
+ d = makedev(dev.major, dev.minor + (i * dev.delta));
+ if (isDebug())
+ msgDebug("deviceTry: Making %s device for %s [%d, %d]\n", m & S_IFCHR ? "raw" : "block", try, dev.major, dev.minor + (i * dev.delta));
+ fail = mknod(try, m, d);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded on second try.\n", try);
+ return fd;
+ }
+ else if (!fail)
+ (void)unlink(try);
+ /* Don't try a "make-under" here since we're using a fixit floppy in this case */
+ snprintf(try, FILENAME_MAX, "/mnt/dev/%s", unit);
+ fd = open(try, O_RDONLY);
+ if (isDebug())
+ msgDebug("deviceTry: final attempt for %s returns %d\n", try, fd);
+ 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_TAPE:
+ 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_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
+ if (isDebug())
+ msgDebug("Found a TAPE device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_DISK:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0 && RunningAsInit) {
+ dev_t d;
+ mode_t m;
+ int s, fail;
+ char unit[80], slice[80];
+
+ close(fd);
+ /* Make associated slice entries */
+ for (s = 1; s < 8; s++) {
+ snprintf(unit, sizeof unit, device_names[i].name, j);
+ snprintf(slice, sizeof slice, "/dev/%ss%d", unit, s);
+ d = makedev(device_names[i].major, device_names[i].minor +
+ (j * device_names[i].delta) + (s * SLICE_DELTA));
+ m = 0640 | S_IFCHR;
+ fail = mknod(slice, m, d);
+ fd = open(slice, O_RDONLY);
+ if (fd >= 0)
+ close(fd);
+ else if (!fail)
+ (void)unlink(slice);
+ }
+ }
+ 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;
+
+ 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/sade/disks.c b/usr.sbin/sade/disks.c
new file mode 100644
index 0000000..535fb05
--- /dev/null
+++ b/usr.sbin/sade/disks.c
@@ -0,0 +1,996 @@
+/*
+ * 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 <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 */
+static struct chunk *chunk_info[16];
+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
+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;
+#ifdef PC98
+ if (d->bios_cyl >= 65536 || d->bios_hd > 16 || d->bios_sect >= 256) {
+#else
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+#endif
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %lu/%lu/%lu for %s is incorrect. Using\n"
+ "a more likely geometry. If this geometry is incorrect or you\n"
+ "are unsure as to whether or not it's correct, please consult\n"
+ "the Hardware Guide in the Documentation submenu or use the\n"
+ "(G)eometry command to change it now.\n\n"
+ "Remember: you need to enter whatever your BIOS thinks the\n"
+ "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''.",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ }
+ 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()
+{
+ 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;
+ 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);
+
+ 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__) && !defined(__ia64__)
+ 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':
+ /* 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;
+
+#ifndef __ia64__
+ 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;
+#endif
+
+ 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(__ia64__) || 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) {
+ 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..ee9fea4
--- /dev/null
+++ b/usr.sbin/sade/dispatch.c
@@ -0,0 +1,442 @@
+/*
+ * 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_mediaClose(dialogMenuItem *unused);
+
+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 },
+ { "configXSetup", configXSetup },
+ { "configXDesktop", configXDesktop },
+#ifdef WITH_SLICES
+ { "diskPartitionEditor", diskPartitionEditor },
+#endif
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distUnsetCustom", distUnsetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "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 },
+ { "mediaClose", dispatch_mediaClose },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "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_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;
+ }
+ /* 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;
+}
+
+
+/*
+ * 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)) {
+
+ if ((cp = strchr(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\n"
+ "residing on a MSDOS or UFS floppy.", 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;
+}
+
diff --git a/usr.sbin/sade/dmenu.c b/usr.sbin/sade/dmenu.c
new file mode 100644
index 0000000..d89f8ce
--- /dev/null
+++ b/usr.sbin/sade/dmenu.c
@@ -0,0 +1,319 @@
+/*
+ * 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 <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
+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, *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 (*((unsigned 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;
+}
+
+/* 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..4323bd0
--- /dev/null
+++ b/usr.sbin/sade/globals.c
@@ -0,0 +1,73 @@
+/*
+ * 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? */
+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 */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+/*
+ * 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;
+}
diff --git a/usr.sbin/sade/help/partition.hlp b/usr.sbin/sade/help/partition.hlp
new file mode 100644
index 0000000..1d62148
--- /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 80MB or more Yes
+
+Note: If you do not create a /usr filesystem then your / filesystem
+will need to be bigger - at least 200MB. 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..b6f6a83
--- /dev/null
+++ b/usr.sbin/sade/help/slice.hlp
@@ -0,0 +1,65 @@
+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'. 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. If you select the default of `Yes', 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, you can select `No' at the
+compatibility prompt. 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'.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ '>' -- This slice doesn't end before cylinder 1024
+ 'R' -- This slice contains the root (/) filesystem
+ 'B' -- Slice employs BAD144 bad-spot handling
+ 'C' -- This is the FreeBSD 2.0-compatibility slice (default)
+ 'A' -- This slice is marked active.
+
+If you select a slice for Bad144 handling, it will be scanned
+for bad blocks before any new filesystems are made on it.
+
+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'.
+
+Final Note: If you're absolutely sure you know what you're doing
+ and you want to use the old "Dangerously Dedicated" mode
+ which is now deprecated by sysinstall, use the (purposely)
+ undocumented `F' key.
+
diff --git a/usr.sbin/sade/install.c b/usr.sbin/sade/install.c
new file mode 100644
index 0000000..e52d0e2
--- /dev/null
+++ b/usr.sbin/sade/install.c
@@ -0,0 +1,1261 @@
+/*
+ * 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/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 <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;
+
+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, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **tdev, Chunk **hdev)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev, *tmpdev, *homedev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ if (rdev)
+ *rdev = NULL;
+ if (sdev)
+ *sdev = NULL;
+ if (udev)
+ *udev = NULL;
+ if (vdev)
+ *vdev = NULL;
+ if (tdev)
+ *tdev = NULL;
+ if (hdev)
+ *hdev = NULL;
+ rootdev = swapdev = usrdev = vardev = tmpdev = homedev = NULL;
+
+ /* 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
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
+ if (rootdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one root device set?!\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ rootdev = c2;
+ if (isDebug())
+ msgDebug("Found rootdev at %s!\n", rootdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
+ if (usrdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /usr filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ usrdev = c2;
+ if (isDebug())
+ msgDebug("Found usrdev at %s!\n", usrdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
+ if (vardev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /var filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ vardev = c2;
+ if (isDebug())
+ msgDebug("Found vardev at %s!\n", vardev->name);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/tmp")) {
+ if (tmpdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /tmp filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ tmpdev = c2;
+ if (isDebug())
+ msgDebug("Found tmpdev at %s!\n", tmpdev->name);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/home")) {
+ if (homedev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /home filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ homedev = c2;
+ if (isDebug())
+ msgDebug("Found homedev at %s!\n", homedev->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 && !swapdev) {
+ swapdev = c2;
+ if (isDebug())
+ msgDebug("Found swapdev at %s!\n", swapdev->name);
+ break;
+ }
+#ifndef __ia64__
+ }
+ }
+#endif
+ }
+ }
+
+ /* Copy our values over */
+ if (rdev)
+ *rdev = rootdev;
+ if (sdev)
+ *sdev = swapdev;
+ if (udev)
+ *udev = usrdev;
+ if (vdev)
+ *vdev = vardev;
+ if (tdev)
+ *tdev = tmpdev;
+ if (hdev)
+ *hdev = homedev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && 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;
+ }
+ 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();
+ return DITEM_SUCCESS;
+ FixItMode = 0;
+}
+
+int
+installFixitCDROM(dialogMenuItem *self)
+{
+ struct stat sb;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+ (void)unlink("/mnt2");
+ (void)rmdir("/mnt2");
+
+ while (1) {
+ 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 (msgYesNo("Unable to mount the disc - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+
+ /* 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();
+ 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);
+ }
+
+ /* 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();
+ }
+ 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 (200MB 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 (200MB 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_LINUX
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to enable Linux binary compatibility?"))
+ (void)configLinux(self);
+#endif
+
+#ifdef __alpha__
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to enable OSF/1 binary compatibility?"))
+ (void)configOSF1(self);
+#endif
+
+#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();
+
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to configure your X server at this time?"))
+ (void)configXSetup(self);
+ }
+
+ 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 just the CRYPTO 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 (!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;
+ int kstat = 1;
+
+ /* 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 (!kstat || !OnVTY)
+ fprintf(fp, "# -- sysinstall generated deltas -- #\n");
+ if (!kstat)
+ fprintf(fp, "userconfig_script_load=\"YES\"\n");
+ if (!OnVTY)
+ 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 build for bin */
+ vsystem("newaliases");
+
+ /* 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");
+
+ /* Do all the last ugly work-arounds here */
+ }
+ 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, *rootdev, *swapdev;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ Boolean upgrade = FALSE;
+#if defined(__ia64__)
+ char efi_bootdir[FILENAME_MAX];
+#endif
+
+ /* 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, &rootdev, &swapdev, NULL, NULL, NULL, NULL))
+ return DITEM_FAILURE;
+
+ if (rootdev)
+ root = (PartInfo *)rootdev->private_data;
+ else
+ root = NULL;
+
+ command_clear();
+ if (swapdev && RunningAsInit) {
+ /* As the very first thing, try to get ourselves some swap space */
+ sprintf(dname, "/dev/%s", swapdev->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 (rootdev && RunningAsInit) {
+ /* Next, create and/or mount the root device */
+ sprintf(dname, "/dev/%s", rootdev->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", rootdev->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. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ 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", rootdev->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 == rootdev)
+ 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 == swapdev)
+ 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);
+
+ /*
+ * Create a directory boot on the EFI filesystem and create a
+ * link boot on the root filesystem pointing to the one on the
+ * EFI filesystem. That way, we install the loader, kernel
+ * and modules on the EFI filesystem.
+ */
+ sprintf(bootdir, "%s", RunningAsInit ? "/mnt" : "");
+ sprintf(efi_bootdir, "%s/%s", bootdir, pi->mountpoint);
+ strcat(bootdir, "/boot");
+ strcat(efi_bootdir, "/boot");
+ command_func_add(pi->mountpoint, Mkdir_command, efi_bootdir);
+
+ /* Make a relative link. */
+ p = &efi_bootdir[(RunningAsInit) ? 4 : 0];
+ while (*p == '/')
+ p++;
+ symlink(p, bootdir);
+ }
+#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)
+{
+ char *cp;
+
+ /* Set default startup options */
+ variable_set2(VAR_RELNAME, getRelname(), 0);
+ variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
+ variable_set2(VAR_TAPE_BLOCKSIZE, DEFAULT_TAPE_BLOCKSIZE, 0);
+ variable_set2(VAR_INSTALL_ROOT, "/", 0);
+ variable_set2(VAR_INSTALL_CFG, "install.cfg", 0);
+ variable_set2(VAR_SKIP_PCCARD, "NO", 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);
+ 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/sade/keymap.c b/usr.sbin/sade/keymap.c
new file mode 100644
index 0000000..3c53a00
--- /dev/null
+++ b/usr.sbin/sade/keymap.c
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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/sade/label.c b/usr.sbin/sade/label.c
new file mode 100644
index 0000000..3f27e589
--- /dev/null
+++ b/usr.sbin/sade/label.c
@@ -0,0 +1,1706 @@
+/*
+ * 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 <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 80
+#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 256
+#define USR_DEFAULT_SIZE 3072
+#define VAR_DEFAULT_SIZE 256
+#define TMP_DEFAULT_SIZE 256
+#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 192
+#define USR_NOMINAL_SIZE 512
+#define VAR_NOMINAL_SIZE 64
+#define TMP_NOMINAL_SIZE 64
+#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(char *mpoint, Boolean newfs)
+{
+ PartInfo *pi;
+
+ if (!mpoint)
+ mpoint = "/change_me";
+
+ pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
+
+ pi->do_newfs = newfs;
+
+ 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;
+}
+
+#if defined(__ia64__)
+static PartInfo *
+new_efi_part(char *mpoint, Boolean newfs)
+{
+ PartInfo *pi;
+
+ if (!mpoint)
+ mpoint = "/efi";
+
+ pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
+
+ pi->do_newfs = newfs;
+ pi->newfs_type = NEWFS_MSDOS;
+
+ return pi;
+}
+#endif
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+ Boolean newfs;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "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(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)
+{
+ extern void print_label_chunks();
+ 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(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].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].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(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);
+ }
+#if defined(__ia64__)
+ else if (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_efi_part(pi->mountpoint, !pi->do_newfs);
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+#endif
+ 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"
+ "sysinstall first.");
+ }
+ 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.
+ *
+ * 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;
+ struct chunk *root_chunk = NULL;
+ struct chunk *swap_chunk = NULL;
+ struct chunk *usr_chunk = NULL;
+ struct chunk *var_chunk = NULL;
+ struct chunk *tmp_chunk = NULL;
+ struct chunk *home_chunk = NULL;
+ int mib[2];
+ unsigned long physmem;
+ size_t size;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+ Chunk *tmpdev, *homedev;
+ 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, &rootdev, &swapdev, &usrdev,
+ &vardev, &tmpdev, &homedev);
+ if (!rootdev) {
+ sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
+
+ root_chunk = 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 (!root_chunk) {
+ *req = 1;
+ msg = "Unable to create the root partition. Too big?";
+ goto done;
+ }
+ root_chunk->private_data = new_part("/", TRUE);
+ root_chunk->private_free = safe_free;
+ root_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!swapdev) {
+ 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;
+ }
+ swap_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_SWAP, CHUNK_AUTO_SIZE);
+ if (!swap_chunk) {
+ *req = 1;
+ msg = "Unable to create the swap partition. Too big?";
+ goto done;
+ }
+ swap_chunk->private_data = 0;
+ swap_chunk->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ if (!vardev) {
+ sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, VAR_DEFAULT_SIZE, perc);
+
+ var_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!var_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /var - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ var_chunk->private_data = new_part("/var", TRUE);
+ var_chunk->private_free = safe_free;
+ var_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!tmpdev && !variable_get(VAR_NO_TMP)) {
+ sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
+
+ tmp_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!tmp_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /tmp - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ tmp_chunk->private_data = new_part("/tmp", TRUE);
+ tmp_chunk->private_free = safe_free;
+ tmp_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!usrdev && !variable_get(VAR_NO_USR)) {
+ sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
+#if AUTO_HOME == 0
+ 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!";
+ }
+
+ usr_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!usr_chunk) {
+ 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;
+ }
+ usr_chunk->private_data = new_part("/usr", TRUE);
+ usr_chunk->private_free = safe_free;
+ usr_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#if AUTO_HOME == 1
+ if (!homedev && !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;
+ }
+
+ home_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!home_chunk) {
+ 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;
+ }
+ home_chunk->private_data = new_part("/home", TRUE);
+ home_chunk->private_free = safe_free;
+ home_chunk->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 (root_chunk)
+ Delete_Chunk(root_chunk->disk, root_chunk);
+ if (swap_chunk)
+ Delete_Chunk(swap_chunk->disk, swap_chunk);
+ if (var_chunk)
+ Delete_Chunk(var_chunk->disk, var_chunk);
+ if (tmp_chunk)
+ Delete_Chunk(tmp_chunk->disk, tmp_chunk);
+ if (usr_chunk)
+ Delete_Chunk(usr_chunk->disk, usr_chunk);
+ if (home_chunk)
+ Delete_Chunk(home_chunk->disk, home_chunk);
+ 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(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(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..8300173
--- /dev/null
+++ b/usr.sbin/sade/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/sade/main.c b/usr.sbin/sade/main.c
new file mode 100644
index 0000000..695754b
--- /dev/null
+++ b/usr.sbin/sade/main.c
@@ -0,0 +1,187 @@
+/*
+ * 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>
+
+const char *StartName; /* Initial contents of argv[0] */
+
+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;
+
+ /* 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;
+ }
+
+#ifdef PC98
+ {
+ /* XXX */
+ char *p = getenv("TERM");
+ if (p && strcmp(p, "cons25") == 0)
+ putenv("TERM=cons25w");
+ }
+#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");
+ }
+
+ /* Initialize PC Card, if we haven't already done so. */
+#ifdef PCCARD_ARCH
+ if (!variable_cmp(VAR_SKIP_PCCARD, "YES") &&
+ variable_get(VAR_SKIP_PCCARD)!=1 &&
+ !pvariable_get("pccardInitialize")) {
+ pccardInitialize();
+ pvariable_set("pccardInitialize=1");
+ }
+#endif
+
+ /* Initialize USB, if we haven't already done so. */
+ if (!pvariable_get("usbInitialize")) {
+ usbInitialize();
+ pvariable_set("usbInitialize=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);
+ }
+
+ /* 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(__alpha__) || 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\n"
+ "(be sure to remove any floppies/CDs/DVDs from the drives).")
+#endif
+ )
+ break;
+ }
+
+ /* Say goodnight, Gracie */
+ systemShutdown(0);
+
+ return 0; /* We should never get here */
+}
diff --git a/usr.sbin/sade/menus.c b/usr.sbin/sade/menus.c
new file mode 100644
index 0000000..6830754
--- /dev/null
+++ b/usr.sbin/sade/menus.c
@@ -0,0 +1,2283 @@
+/*
+ * 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;
+ CRYPTODists |= (DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ CRYPTODists &= ~(DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Misc(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_MISC_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Misc(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_MISC_ALL;
+ if (!XF86ServerDists && !XF86FontDists)
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Servers(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_SERVER;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Servers(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_SERVER;
+ XF86ServerDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_FONTS;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_FONTS;
+ XF86FontDists = 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
+checkDistXDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_ALL);
+}
+
+static int
+checkDistKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistXKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, 0);
+}
+
+static int
+checkDistXUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, DIST_XF86);
+}
+
+static int
+checkDistMinimum(dialogMenuItem *self)
+{
+ return Dists == (DIST_BASE | DIST_CRYPTO);
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+ return Dists == DIST_ALL && CRYPTODists == DIST_CRYPTO_ALL &&
+ _IS_SET(SrcDists, DIST_SRC_ALL) &&
+ _IS_SET(XF86Dists, DIST_XF86_ALL) &&
+ _IS_SET(XF86ServerDists, DIST_XF86_SERVER_ALL) &&
+ _IS_SET(XF86FontDists, DIST_XF86_FONTS_ALL);
+}
+
+static int
+srcFlagCheck(dialogMenuItem *item)
+{
+ return SrcDists;
+}
+
+static int
+x11FlagCheck(dialogMenuItem *item)
+{
+ return Dists & DIST_XF86;
+}
+
+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 },
+#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", "Load default settings.", NULL, dispatch_load_floppy },
+#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, X Developer", "Select X developer's distribution.", checkDistXDeveloper, distSetXDeveloper },
+ { " Dists, Kern Developer", "Select kernel developer's distribution.", checkDistKernDeveloper, distSetKernDeveloper },
+ { " Dists, User", "Select average user distribution.", checkDistUser, distSetUser },
+ { " Dists, X User", "Select average X user distribution.", checkDistXUser, distSetXUser },
+ { " Distributions, Adding", "Installing additional distribution sets", NULL, distExtractAll },
+ { " Distributions, XFree86","XFree86 distribution menu.", NULL, distSetXF86 },
+ { " Documentation", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { " Doc, README", "The distribution README file.", NULL, dmenuDisplayFile, NULL, "README" },
+ { " Doc, Early Adopter's", "Early Adopter's Guide to FreeBSD 5.0.", NULL, dmenuDisplayFile, NULL, "EARLY" },
+ { " Doc, Errata", "The distribution errata.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { " Doc, Hardware", "The distribution hardware guide.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { " Doc, Install", "The distribution installation guide.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { " 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, Tape", "Select tape installation media.", NULL, mediaSetTape },
+ { " 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, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { " 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 },
+ { " XFree86, Fonts", "XFree86 Font selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { " XFree86, Server", "XFree86 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { NULL } },
+};
+
+/* 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.",
+ "Press F1 for Installation Guide", /* help line */
+ "INSTALL", /* help file */
+ { { " 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, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+#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_floppy },
+ { "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 Early Adopter's", "Early Adopter's Guide to FreeBSD 5.0.", NULL, dmenuDisplayFile, NULL, "EARLY" },
+ { "3 Errata", "Late-breaking, post-release news.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { "4 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { "5 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { "6 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "7 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { "8 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "9 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/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { 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/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { "4 COM3", "Serial mouse on COM3 (/dev/cuaa2)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa2" },
+ { "5 COM4", "Serial mouse on COM4 (/dev/cuaa3)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa3" },
+ { "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.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { 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.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { 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!",
+ "INSTALL",
+ { { "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", "snapshots.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://snapshots.jp.freebsd.org" },
+
+ { "IPv6 Ireland", "ftp3.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ie.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" },
+
+ { "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" },
+
+ { "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" },
+
+ { "Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hk.super.net" },
+
+ { "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" },
+
+ { "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" },
+
+ { "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 MenuMediaTape = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a tape drive type",
+ "FreeBSD can be installed from tape drive, though this installation\n"
+ "method requires a certain amount of temporary storage in addition\n"
+ "to the space required by the distribution itself (tape drives make\n"
+ "poor random-access devices, so we extract _everything_ on the tape\n"
+ "in one pass). If you have sufficient space for this, then you should\n"
+ "select one of the following tape devices detected on your system.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { 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 } },
+};
+
+/* 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 },
+ { "9 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "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, binaries and X Window System",
+ 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 X-Developer", "Same as above + X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "6 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "7 X-Kern-Developer", "Same as above + X Window System",
+ checkDistXKernDeveloper, distSetXKernDeveloper },
+ { "8 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "9 X-User", "Same as above + X Window System",
+ checkDistXUser, distSetXUser },
+ { "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, binaries and X Window System",
+ 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 },
+#ifdef __i386__
+ { " compat1x", "FreeBSD 1.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT1X },
+ { " compat20", "FreeBSD 2.0 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT20 },
+ { " compat21", "FreeBSD 2.1 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT21 },
+ { " compat22", "FreeBSD 2.2.x and 3.0 a.out binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT22 },
+ { " compat3x", "FreeBSD 3.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT3X },
+#endif
+#if __FreeBSD__ >= 4 && (defined(__i386__) || defined(__alpha__))
+ { " compat4x", "FreeBSD 4.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT4X },
+#endif
+ { " crypto", "Basic encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_CRYPTO, },
+ { " 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 },
+ { " 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},
+ { " perl", "The Perl distribution",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PERL },
+ { " XFree86", "The XFree86 distribution",
+ x11FlagCheck, distSetXF86 },
+ { 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 },
+ { " contrib", "/usr/src/contrib (contributed software)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CONTRIB },
+ { " 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 },
+ { " 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 },
+ { " 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 },
+ { " scrypto", "/usr/src/crypto (contrib encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SCRYPTO },
+ { " share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { " skrb5", "/usr/src/kerberos5 (sources for Kerberos5)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SKERBEROS5 },
+ { " ssecure", "/usr/src/secure (BSD encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SSECURE },
+ { " 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 MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first option, xf86cfg, is fully graphical.\n"
+ "The second option provides a menu-based interface similar to\n"
+ "what you are currently using. "
+ "The third option, xf86config, is\n"
+ "a more simplistic shell-script based tool and less friendly to\n"
+ "new users, but it may work in situations where the other options\n"
+ "do not.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 xf86cfg", "Fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg" },
+ { "3 xf86cfg -textmode", "ncurses-based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg -textmode" },
+ { "4 xf86config", "Shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { "D XDesktop", "X already set up, just do desktop configuration.",
+ NULL, dmenuSubmenu, NULL, &MenuXDesktops },
+ { NULL } },
+};
+
+DMenu MenuXDesktops = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the default X desktop to use.",
+ "By default, XFree86 comes with a fairly vanilla desktop which\n"
+ "is based around the twm(1) window manager and does not offer\n"
+ "much in the way of features. It does have the advantage of\n"
+ "being a standard part of X so you don't need to load anything\n"
+ "extra in order to use it. If, however, you have access to a\n"
+ "reasonably full packages collection on your installation media,\n"
+ "you can choose any one of the following desktops as alternatives.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 KDE", "The K Desktop Environment (Lite Edition)",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=kde" },
+ { "3 GNOME 2", "The GNOME 2 Desktop Environment (Lite Edition)",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=gnome2" },
+ { "4 Afterstep", "The Afterstep window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=afterstep" },
+ { "5 Windowmaker", "The Windowmaker window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=windowmaker" },
+ { "6 fvwm", "The fvwm window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=fvwm2" },
+ { NULL } },
+};
+
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 Distribution",
+ "Please select the components you need from the XFree86\n"
+ "distribution sets.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "Basic", "Basic component menu (required)", NULL, dmenuSubmenu, NULL, &MenuXF86SelectCore },
+ { "Server", "X server menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "Fonts", "Font set menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 base distribution types",
+ "Please check off the basic XFree86 components you wish to install.\n"
+ "Bin, lib, and set are recommended for a minimum installaion.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all below",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all below",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { " bin", "Client applications",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CLIENTS },
+ { " lib", "Shared libraries and data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+ { " man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { " doc", "Documentation",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_DOC },
+ { " prog", "Programming tools",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectFonts = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Font distribution selection.",
+ "Please check off the individual font distributions you wish to\n\
+install. At the minimum, you should install the standard\n\
+75 DPI and misc fonts if you're also installing a server\n\
+(these are selected by default).",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { " fnts", "Standard miscellaneous fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_BITMAPS },
+ { " f75", "75 DPI fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_75 },
+ { " f100", "100 DPI fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_100 },
+ { " fcyr", "Cyrillic Fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_CYR },
+ { " fscl", "Speedo and Type scalable fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SCALE },
+ { " server", "Font server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SERVER },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectServer = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "X Server selection.",
+ "Please check off the types of X servers you wish to install.\n",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { " srv", "Standard Graphics Framebuffer",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_FB },
+ { " nest", "Nested X Server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_NEST },
+ { " prt", "X Print Server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_PRINT },
+ { " vfb", "Virtual Framebuffer",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VFB },
+ { 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.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { "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 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 },
+ { "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 } },
+};
+#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 },
+ { " XFree86", "Configure XFree86 Server",
+ NULL, configXSetup },
+ { " Desktop", "Configure XFree86 Desktop",
+ NULL, configXDesktop },
+ { " 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
+#ifdef PCCARD_ARCH
+ { " pccard", "Enable PCCARD (AKA PCMCIA) services (also laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "pccard_enable=YES" },
+ { " pccard mem", "Set PCCARD memory address (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_mem" },
+ { " pccard ifconfig", "List of PCCARD ethernet devices to configure",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_ifconfig" },
+#endif
+ { " usbd", "Enable USB daemon (detect USB attach / detach)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "usbd_enable=YES" },
+ { " usbd flags", "Set default flags to usbd (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "usbd_flags" },
+ { " ", " -- ", 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 WITH_LINUX
+ { " Linux", "This host wants to be able to run Linux binaries.",
+ dmenuVarCheck, configLinux, NULL, VAR_LINUX_ENABLE "=YES" },
+#endif
+#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
+#ifdef __alpha__
+ { " OSF/1", "This host wants to be able to run DEC OSF/1 binaries.",
+ dmenuVarCheck, configOSF1, NULL, VAR_OSF1_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 },
+ { "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", "augean.eleceng.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=augean.eleceng.adelaide.edu.au" },
+ { "Australia #2", "ntp.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.adelaide.edu.au" },
+ { "Australia #3", "ntp.saard.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.saard.net" },
+ { "Australia #4", "time.deakin.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.deakin.edu.au" },
+ { "Australia #5", "time.esec.com.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.esec.com.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", "ntp.cais.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cais.rnp.br" },
+ { "Brazil #2", "ntp.pop-df.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.pop-df.rnp.br" },
+ { "Brazil #3", "ntp.ufes.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ufes.br" },
+ { "Brazil #4", "ntp1.pucpr.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.pucpr.br" },
+ { "Canada", "ntp.cpsc.ucalgary.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cpsc.ucalgary.ca" },
+ { "Canada #2", "ntp1.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cmc.ec.gc.ca" },
+ { "Canada #3", "ntp2.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.cmc.ec.gc.ca" },
+ { "Canada #4", "tick.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.utoronto.ca" },
+ { "Canada #5", "time.chu.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.chu.nrc.ca" },
+ { "Canada #6", "time.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nrc.ca" },
+ { "Canada #7", "timelord.uregina.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timelord.uregina.ca" },
+ { "Canada #8", "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" },
+ { "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.incaf.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.incaf.net" },
+ { "Ireland", "ntp.maths.tcd.ie",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.maths.tcd.ie" },
+ { "Italy", "ntps.net4u.it",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=" },
+ { "Japan", "ntp.cyber-fleet.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cyber-fleet.net" },
+ { "Korea", "time.nuri.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nuri.net" },
+ { "Mexico", "ntp2a.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.audiotel.com.mx" },
+ { "Mexico #2", "ntp2b.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.audiotel.com.mx" },
+ { "Mexico #3", "ntp2c.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.audiotel.com.mx" },
+ { "Nigeria", "ntp.supernet300.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.supernet300.com" },
+ { "Netherlands", "ntp1.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.theinternetone.net" },
+ { "Netherlands #2", "ntp2.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.theinternetone.net" },
+ { "Netherlands #3", "ntp3.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp3.theinternetone.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" },
+ { "Portugal", "bug.fe.up.pt",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bug.fe.up.pt" },
+ { "Romania", "ntp.ip.ro",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ip.ro" },
+ { "Russia", "ntp.psn.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.psn.ru" },
+ { "Russia #2", "sign.chg.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sign.chg.ru" },
+ { "Sweden", "ntp.lth.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.lth.se" },
+ { "Singapore", "ntp.shim.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.shim.org" },
+ { "Slovenia", "calvus.rzs-hm.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=calvus.rzs-hm.si" },
+ { "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" },
+ { "United Kingdom", "ntp.exnet.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.exnet.com" },
+ { "United Kingdom #2", "ntp0.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.uk.uu.net" },
+ { "United Kingdom #3", "ntp1.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.uk.uu.net" },
+ { "United Kingdom #4", "ntp2.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.uk.uu.net" },
+ { "United Kingdom #5", "ntp2a.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.mcc.ac.uk" },
+ { "United Kingdom #6", "ntp2b.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.mcc.ac.uk" },
+ { "United Kingdom #7", "ntp2c.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.mcc.ac.uk" },
+ { "United Kingdom #8", "ntp2d.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2d.mcc.ac.uk" },
+ { "United Kingdom #9", "tick.tanac.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.tanac.net" },
+ { "U.S. AR", "sushi.compsci.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", "clock.linuxshell.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.linuxshell.net" },
+ { "U.S. NY #2", "ntp.ctr.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ctr.columbia.edu" },
+ { "U.S. NY #3", "ntp0.cornell.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.cornell.edu" },
+ { "U.S. NY #4", "ntp1.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.mpis.net" },
+ { "U.S. NY #5", "ntp2.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.mpis.net" },
+ { "U.S. NY #6", "sundial.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sundial.columbia.edu" },
+ { "U.S. NY #7", "timex.cs.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.cs.columbia.edu" },
+ { "U.S. OK", "constellation.ecn.uoknor.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=constellation.ecn.uoknor.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.cox.smu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cox.smu.edu" },
+ { "U.S. TX #2", "ntp.fnbhs.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.fnbhs.com" },
+ { "U.S. TX #3", "ntp.tmc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tmc.edu" },
+ { "U.S. TX #4", "ntp5.tamu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp5.tamu.edu" },
+ { "U.S. TX #5", "tick.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.greyware.com" },
+ { "U.S. TX #6", "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", "clock.tricity.wsu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tricity.wsu.edu" },
+ { "U.S. WA #2", "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" },
+ { "Venezuela", "ntp.linux.org.ve",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.linux.org.ve" },
+ { "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" },
+ { " 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" },
+ { "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 phosphor 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 used 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 firwalling 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/sade/misc.c b/usr.sbin/sade/misc.c
new file mode 100644
index 0000000..07cbc81
--- /dev/null
+++ b/usr.sbin/sade/misc.c
@@ -0,0 +1,529 @@
+/*
+ * 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>
+
+/* 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);
+}
+
diff --git a/usr.sbin/sade/msg.c b/usr.sbin/sade/msg.c
new file mode 100644
index 0000000..4625ce2
--- /dev/null
+++ b/usr.sbin/sade/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/sade/rtermcap.c b/usr.sbin/sade/rtermcap.c
new file mode 100644
index 0000000..84b3feb
--- /dev/null
+++ b/usr.sbin/sade/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/sade/sade.8 b/usr.sbin/sade/sade.8
new file mode 100644
index 0000000..b1b04bb
--- /dev/null
+++ b/usr.sbin/sade/sade.8
@@ -0,0 +1,1010 @@
+.\" 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 9, 1997
+.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's 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 configXSetup
+Configure the X display subsystem.
+.Pp
+.Sy Variables :
+None
+.It configXDesktop
+Configure the X desktop.
+.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. 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've 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 500MB 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 40960 /"
+A 20MB root file system (all sizes are in 512 byte blocks).
+.It Li "da0s2-2=swap 131072 /"
+A 64MB swap partition.
+.It Li "da0s2-3=ufs 204800 /var"
+A 100MB /var file system.
+.It Li "da0s2-4=ufs 0 /usr 1"
+With the balance of free space (around 316MB) 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 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 crypto
+Encryption binaries and libraries.
+.It Li compat1x
+Compatibility with
+.Fx
+1.x
+.It Li compat20
+Compatibility with
+.Fx 2.0
+.It Li compat21
+Compatibility with
+.Fx 2.1
+.It Li compat22
+.Fx 2.2
+and
+.Fx 3.0
+a.out binary compatibility
+.It Li compat3x
+Compatibility with
+.Fx
+3.x
+(available for
+.Fx 4.0
+systems only)
+.It Li compat4x
+Compatibility with
+.Fx
+4.x
+(available for
+.Fx 5.0
+systems only)
+.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 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 sbin
+/usr/src/bin
+.It Li ssbin
+/usr/src/sbin
+.It Li sshare
+/usr/src/share
+.It Li ssys
+/usr/src/sys
+.It Li subin
+/usr/src/usr.bin
+.It Li susbin
+/usr/src/usr.sbin
+.It Li ssmailcf
+/usr/src/usr.sbin/sendmail/cf
+.It Li XF86-xc
+XFree86 official sources.
+.It Li XF86-co
+XFree86 contributed sources.
+.It Li Xbin
+XFree86 binaries.
+.It Li Xcfg
+XFree86 configuration files.
+.It Li Xdoc
+XFree86 documentation.
+.It Li Xhtml
+XFree86 HTML documentation.
+.It Li Xlib
+XFree86 libraries.
+.It Li Xlk98
+XFree86 server link-kit for PC98 machines.
+.It Li Xlkit
+XFree86 server link-kit for standard machines.
+.It Li Xman
+XFree86 manual pages.
+.It Li Xprog
+XFree86 programmer's distribution.
+.It Li Xps
+XFree86 postscript documentation.
+.It Li Xset
+XFree86 graphical setup tool.
+.It Li PC98-Servers/X9480
+XFree86 PC98 8-bit (256 color) PEGC-480 server.
+.It Li PC98-Servers/X9EGC
+XFree86 PC98 4-bit (16 color) EGC server.
+.It Li PC98-Servers/X9GA9
+XFree86 PC98 GA-968V4/PCI (S3 968) server.
+.It Li PC98-Servers/X9GAN
+XFree86 PC98 GANB-WAP (cirrus) server.
+.It Li PC98-Servers/X9LPW
+XFree86 PC98 PowerWindowLB (S3) server.
+.It Li PC98-Servers/X9MGA
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9NKV
+XFree86 PC98 NKV-NEC (cirrus) server.
+.It Li PC98-Servers/X9NS3
+XFree86 PC98 NEC (S3) server.
+.It Li PC98-Servers/X9SPW
+XFree86 PC98 SKB-PowerWindow (S3) server.
+.It Li PC98-Servers/X9SVG
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9TGU
+XFree86 PC98 Cyber9320 and TGUI9680 server.
+.It Li PC98-Servers/X9WEP
+XFree86 PC98 WAB-EP (cirrus) server.
+.It Li PC98-Servers/X9WS
+XFree86 PC98 WABS (cirrus) server.
+.It Li PC98-Servers/X9WSN
+XFree86 PC98 WSN-A2F (cirrus) server.
+.It Li Servers/X3DL
+XFree86 3D Labs server.
+.It Li Servers/X8514
+XFree86 8514 server.
+.It Li Servers/XAGX
+XFree86 8 bit AGX server.
+.It Li Servers/XI128
+XFree86 #9 Imagine I128 server.
+.It Li Servers/XMa8
+XFree86 ATI Mach8 server.
+.It Li Servers/XMa32
+XFree86 ATI Mach32 server.
+.It Li Servers/XMa64
+XFree86 ATI Mach64 server.
+.It Li Servers/XMono
+XFree86 monochrome server.
+.It Li Servers/XP9K
+XFree86 P9000 server.
+.It Li Servers/XS3
+XFree86 S3 server.
+.It Li Servers/XS3V
+XFree86 S3 Virge server.
+.It Li Servers/XSVGA
+XFree86 SVGA server.
+.It Li Servers/XVG16
+XFree86 VGA16 server.
+.It Li Servers/XW32
+XFree86 ET4000/W32, /W32i and /W32p server.
+.It Li Servers/XTGA
+Server for TGA cards (alpha architecture only).
+.It Li Servers/Xnest
+XFree86 nested X server.
+.It Li Servers/Xvfb
+XFree86 virtual frame-buffer X server.
+.It Li Xfnts
+XFree86 base font set.
+.It Li Xf100
+XFree86 100DPI font set.
+.It Li Xfcyr
+XFree86 Cyrillic font set.
+.It Li Xfscl
+XFree86 scalable font set.
+.It Li Xfnon
+XFree86 non-english font set.
+.It Li Xfsrv
+XFree86 font server.
+.El
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXUser
+Selects the standard X user's 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 distSetCRYPTO
+Interactively select encryption subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetXF86
+Interactively select XFree86 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 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 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 mediaSetTape
+Select a tape device as the installation media.
+.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're 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're
+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're 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 and terminate sysinstall.
+.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's 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 = 4.6" .
+.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 Alpha packages on an i386 machine.
+For example,
+.Dq Li "CD_MACHINE_ARCH = alpha" .
+.It Va 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 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 BUGS
+This utility is a prototype which lasted several years past
+its expiration date and is greatly in need of death.
+.Sh AUTHORS
+.An Jordan K. Hubbard Aq jkh@FreeBSD.org
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.0 .
diff --git a/usr.sbin/sade/sade.h b/usr.sbin/sade/sade.h
new file mode 100644
index 0000000..938cfad
--- /dev/null
+++ b/usr.sbin/sade/sade.h
@@ -0,0 +1,885 @@
+/*
+ * 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 "libdisk.h"
+#include "dist.h"
+
+/*** Defines ***/
+
+#if defined(__i386__) || defined(__alpha__) || defined(__amd64__)
+#define WITH_SYSCONS
+#define WITH_MICE
+#endif
+
+#if defined(__i386__) || defined(__amd64__)
+#define WITH_SLICES
+#endif
+
+#if defined(__i386__) || defined(__alpha__)
+#define WITH_LINUX
+#endif
+
+#if defined(PC98)
+#define PCCARD_ARCH 1 /* Support PCCARD installations */
+#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_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_CRYPTO "distCRYPTO"
+#define VAR_DIST_SRC "distSRC"
+#define VAR_DIST_X11 "distX11"
+#define VAR_DIST_XSERVER "distXserver"
+#define VAR_DIST_XFONTS "distXfonts"
+#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_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_OSF1_ENABLE "osf1_enable"
+#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_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_TRY_DHCP "tryDHCP"
+#define VAR_TRY_RTSOL "tryRTSOL"
+#define VAR_SKIP_PCCARD "skipPCCARD"
+#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_XF86_CONFIG "_xf86config"
+#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" */
+#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;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_FLOPPY,
+ DEVICE_TYPE_FTP,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_CDROM,
+ DEVICE_TYPE_TAPE,
+ 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)();
+} 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 */
+ 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 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 Device *mediaDevice; /* Where we're getting our distribution from */
+extern unsigned int Dists; /* Which distributions we want */
+extern unsigned int CRYPTODists; /* Which naughty distributions we want */
+extern unsigned int SrcDists; /* Which src distributions we want */
+extern unsigned int XF86Dists; /* Which XFree86 dists we want */
+extern unsigned int XF86ServerDists; /* The XFree86 servers we want */
+extern unsigned int XF86FontDists; /* The XFree86 fonts we want */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+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 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 MenuMediaTape; /* Tape 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 MenuXF86; /* XFree86 main menu */
+extern DMenu MenuXF86Select; /* XFree86 distribution selection menu */
+extern DMenu MenuXF86SelectCore; /* XFree86 core distribution menu */
+extern DMenu MenuXF86SelectServer; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuXDesktops; /* Disk devices menu */
+extern DMenu MenuHTMLDoc; /* HTML Documentation menu */
+extern DMenu MenuUsermgmt; /* User management menu */
+extern DMenu MenuFixit; /* Fixit floppy/CDROM/shell menu */
+extern DMenu MenuXF86Config; /* Select XFree86 configuration type */
+extern int FixItMode; /* FixItMode starts shell onc urrent device (ie Serial port) */
+extern const char * StartName; /* Which name we were started as */
+
+/* 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 ***/
+
+/* 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);
+#ifdef __alpha__
+extern int configOSF1(dialogMenuItem *self);
+#endif
+extern int configUsers(dialogMenuItem *self);
+extern int configXSetup(dialogMenuItem *self);
+extern int configXDesktop(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_file_int(int);
+extern int dispatch_load_file(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 distSetXDeveloper(dialogMenuItem *self);
+extern int distSetKernDeveloper(dialogMenuItem *self);
+extern int distSetXKernDeveloper(dialogMenuItem *self);
+extern int distSetUser(dialogMenuItem *self);
+extern int distSetXUser(dialogMenuItem *self);
+extern int distSetMinimum(dialogMenuItem *self);
+extern int distSetEverything(dialogMenuItem *self);
+extern int distSetSrc(dialogMenuItem *self);
+extern int distSetXF86(dialogMenuItem *self);
+extern int distExtractAll(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 dmenuSetKmapVariable(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 *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 index_initialize(char *path);
+PkgNodePtr index_search(PkgNodePtr top, char *str, PkgNodePtr *tp);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **vtdev, Chunk **hdev);
+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 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 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 void mediaClose(void);
+extern int mediaTimeout(void);
+extern int mediaSetCDROM(dialogMenuItem *self);
+extern int mediaSetFloppy(dialogMenuItem *self);
+extern int mediaSetDOS(dialogMenuItem *self);
+extern int mediaSetTape(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);
+
+/* 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);
+
+/* 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);
+
+/* 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 void systemCreateHoloshell(void);
+extern int vsystem(char *fmt, ...) __printflike(1, 2);
+
+/* tape.c */
+extern char *mediaTapeBlocksize(void);
+extern Boolean mediaInitTape(Device *dev);
+extern FILE *mediaGetTape(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownTape(Device *dev);
+
+/* 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);
+
+/* usb.c */
+extern void usbInitialize(void);
+
+/* 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/sade/system.c b/usr.sbin/sade/system.c
new file mode 100644
index 0000000..65c353a
--- /dev/null
+++ b/usr.sbin/sade/system.c
@@ -0,0 +1,542 @@
+/*
+ * 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 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 "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);
+}
+
+/*
+ * Harvest children if we are init.
+ */
+static void
+reap_children(int sig)
+{
+ int errbak = errno;
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+ errno = errbak;
+}
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ char *gunzip = RunningAsInit ? "/stand/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);
+
+ /* 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:/usr/X11R6/bin", 1);
+ setbuf(stdin, 0);
+ setbuf(stderr, 0);
+#ifdef __alpha__
+ i = 0;
+ sysctlbyname("machdep.unaligned_print", NULL, 0, &i, sizeof(i));
+#endif
+#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)
+ 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(__alpha__) || defined(__sparc64__)
+ reboot(RB_HALT);
+#else
+ reboot(0);
+#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/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.TXT", 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;
+
+ 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 (!RunningAsInit)
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/stand/sh", "/stand/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
+ it serial mode
+ since there is no
+ virtual console */
+ systemResumeDialog();
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sade/termcap.c b/usr.sbin/sade/termcap.c
new file mode 100644
index 0000000..1d8e047
--- /dev/null
+++ b/usr.sbin/sade/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, 80, 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, 80, stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+ *termcapp = (char *)termcap_ansi;
+ }
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct ttysize ts;
+
+ 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, TIOCGSIZE, &ts) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ts.ts_lines = 0;
+ }
+ StatusLine = ts.ts_lines ? ts.ts_lines - 1: (OnVTY ? VTY_STATUS_LINE : TTY_STATUS_LINE);
+ return 0;
+}
diff --git a/usr.sbin/sade/usb.c b/usr.sbin/sade/usb.c
new file mode 100644
index 0000000..4eedbd5
--- /dev/null
+++ b/usr.sbin/sade/usb.c
@@ -0,0 +1,44 @@
+/*
+ * USB support for sysinstall
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 2000 John Baldwin <jhb@FreeBSD.org>. All rights reserved.
+ *
+ * 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
+ * use.
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/time.h>
+
+void
+usbInitialize(void)
+{
+ int fd;
+ WINDOW *w;
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ if ((fd = open("/dev/usb", O_RDONLY)) < 0) {
+ msgDebug("Can't open USB controller.\n");
+ return;
+ }
+ close(fd);
+
+ w = savescr();
+ msgNotify("Initializing USB controller....");
+
+ variable_set2("usbd_enable", "YES", 1);
+
+ vsystem("/stand/usbd");
+ restorescr(w);
+}
diff --git a/usr.sbin/sade/variable.c b/usr.sbin/sade/variable.c
new file mode 100644
index 0000000..e86104f
--- /dev/null
+++ b/usr.sbin/sade/variable.c
@@ -0,0 +1,326 @@
+/*
+ * 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 sysinstall variables to file..\n");
+
+ fp = fopen("/etc/sysinstall.vars", "w");
+ if (!fp) {
+ msgConfirm("Unable to write to /etc/sysinstall.vars: %s",
+ 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 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);
+ putenv(tmp);
+}
+
+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..3a07887
--- /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 "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+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;
+}
+
+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..c1c8381
--- /dev/null
+++ b/usr.sbin/sendmail/Makefile
@@ -0,0 +1,81 @@
+# @(#)Makefile 8.8 (Berkeley) 3/28/97
+# $FreeBSD$
+
+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
+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 \
+ 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
+NIS= -DNIS
+# Map extensions
+MAPS= -DMAP_REGEX -DDNSMAP
+
+CSTD?= c89
+CFLAGS+= -I${SMDIR} -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= ${DBMDEF} ${NIS} -DMILTER -DTCPWRAPPERS ${MAPS}
+
+.if !defined(NOINET6)
+CFLAGS+= -DNETINET6
+.endif
+
+DPADD= ${LIBUTIL} ${LIBWRAP}
+LDADD= -lutil -lwrap
+
+.if exists(${.OBJDIR}/../../lib/libsm)
+LIBSMDIR:= ${.OBJDIR}/../../lib/libsm
+.else
+LIBSMDIR!= cd ${.CURDIR}/../../lib/libsm; make -V .OBJDIR
+.endif
+LIBSM:= ${LIBSMDIR}/libsm.a
+
+.if exists(${.OBJDIR}/../../lib/libsmutil)
+LIBSMUTILDIR:= ${.OBJDIR}/../../lib/libsmutil
+.else
+LIBSMUTILDIR!= cd ${.CURDIR}/../../lib/libsmutil; make -V .OBJDIR
+.endif
+LIBSMUTIL:= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD+= ${LIBSMUTIL} ${LIBSM}
+LDADD+= ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+.if !defined(NOCRYPT) && !defined(NO_OPENSSL) && !defined(RELEASE_CRUNCH)
+# STARTTLS support
+DISTRIBUTION= crypto
+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/setfmac/Makefile b/usr.sbin/setfmac/Makefile
new file mode 100644
index 0000000..bc3c21e
--- /dev/null
+++ b/usr.sbin/setfmac/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= setfmac
+LINKS= ${BINDIR}/setfmac ${BINDIR}/setfsmac
+MAN= setfmac.8 setfsmac.8
+SRCS= setfmac.c
+
+WARNS?= 2
+
+.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..d5f5a61
--- /dev/null
+++ b/usr.sbin/setfmac/setfmac.c
@@ -0,0 +1,506 @@
+/*-
+ * 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/types.h>
+#include <sys/queue.h>
+#include <sys/mac.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 */
+ 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_pathlen,
+ ftsent->fts_path);
+ }
+ if (!qflag)
+ warnx("labeling not supported in %.*s",
+ ftsent->fts_pathlen,
+ 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_pathlen,
+ ftsent->fts_path);
+ default:
+ errx(1, "CANNOT HAPPEN (%d) traversing %.*s",
+ ftsent->fts_info, ftsent->fts_pathlen,
+ 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);
+}
+
+int
+chomp_line(char **line, size_t *linesize)
+{
+ char *s;
+ int freeme = 0;
+
+ for (s = *line; 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_pathlen,
+ 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_pathlen,
+ 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..ca4a918
--- /dev/null
+++ b/usr.sbin/setfmac/setfsmac.8
@@ -0,0 +1,128 @@
+.\" 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 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.
+.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
diff --git a/usr.sbin/setkey/Makefile b/usr.sbin/setkey/Makefile
new file mode 100644
index 0000000..91977e0
--- /dev/null
+++ b/usr.sbin/setkey/Makefile
@@ -0,0 +1,62 @@
+# Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+PROG= setkey
+MAN= setkey.8
+SRCS= setkey.c parse.y token.l
+
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../../lib/libipsec
+YFLAGS= -d
+
+DPADD= ${LIBL} ${LIBY}
+LDADD= -ll -ly
+
+CLEANFILES= y.tab.c y.tab.h key_test.o keytest
+
+# libpfkey
+# ipsec_strerror.c is for avoiding shlib reference to non-exported function.
+.PATH: ${.CURDIR}/../../lib/libipsec ${.CURDIR}/../../sys/netkey
+SRCS+= pfkey.c pfkey_dump.c key_debug.c ipsec_strerror.c
+CFLAGS+= -I${.CURDIR}/../../lib/libipsec -I${.CURDIR}/../../sys/netkey
+
+SRCS+= y.tab.h
+y.tab.h: parse.y
+CFLAGS+= -DIPSEC_DEBUG -DINET6 -DYY_NO_UNPUT -I.
+DPADD+= ${LIBIPSEC}
+LDADD+= -lipsec
+CLEANFILES+= scriptdump y.tab.h
+
+#SCRIPTS= scriptdump
+
+LOCALPREFIX= /usr
+
+scriptdump: scriptdump.pl
+ sed -e 's#@LOCALPREFIX@#${LOCALPREFIX}#' < $> > scriptdump
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setkey/parse.y b/usr.sbin/setkey/parse.y
new file mode 100644
index 0000000..d6bb8c4
--- /dev/null
+++ b/usr.sbin/setkey/parse.y
@@ -0,0 +1,1267 @@
+/* $FreeBSD$ */
+/* $KAME: parse.y,v 1.82 2004/04/15 08:03:57 sakane Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/pfkeyv2.h>
+#include <netkey/key_var.h>
+#include <netinet6/ipsec.h>
+#include <arpa/inet.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "libpfkey.h"
+#include "vchar.h"
+
+#define ATOX(c) \
+ (isdigit(c) ? (c - '0') : (isupper(c) ? (c - 'A' + 10) : (c - 'a' + 10)))
+
+u_int32_t p_spi;
+u_int p_ext, p_alg_enc, p_alg_auth, p_replay, p_mode;
+u_int32_t p_reqid;
+u_int p_key_enc_len, p_key_auth_len;
+caddr_t p_key_enc, p_key_auth;
+time_t p_lt_hard, p_lt_soft;
+
+static int p_aiflags = 0, p_aifamily = PF_UNSPEC;
+
+static struct addrinfo *parse_addr __P((char *, char *));
+static int fix_portstr __P((vchar_t *, vchar_t *, vchar_t *));
+static int setvarbuf __P((char *, int *, struct sadb_ext *, int, caddr_t, int));
+void parse_init __P((void));
+void free_buffer __P((void));
+
+int setkeymsg0 __P((struct sadb_msg *, unsigned int, unsigned int, size_t));
+static int setkeymsg_spdaddr __P((unsigned int, unsigned int, vchar_t *,
+ struct addrinfo *, int, struct addrinfo *, int));
+static int setkeymsg_addr __P((unsigned int, unsigned int,
+ struct addrinfo *, struct addrinfo *, int));
+static int setkeymsg_add __P((unsigned int, unsigned int,
+ struct addrinfo *, struct addrinfo *));
+extern int setkeymsg __P((char *, size_t *));
+extern int sendkeymsg __P((char *, size_t));
+
+extern int yylex __P((void));
+extern void yyfatal __P((const char *));
+extern void yyerror __P((const char *));
+%}
+
+%union {
+ int num;
+ unsigned long ulnum;
+ vchar_t val;
+ struct addrinfo *res;
+}
+
+%token EOT SLASH BLCL ELCL
+%token ADD GET DELETE DELETEALL FLUSH DUMP
+%token PR_ESP PR_AH PR_IPCOMP PR_TCP
+%token F_PROTOCOL F_AUTH F_ENC F_REPLAY F_COMP F_RAWCPI
+%token F_MODE MODE F_REQID
+%token F_EXT EXTENSION NOCYCLICSEQ
+%token ALG_AUTH ALG_AUTH_NOKEY
+%token ALG_ENC ALG_ENC_NOKEY ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_ENC_OLD
+%token ALG_COMP
+%token F_LIFETIME_HARD F_LIFETIME_SOFT
+%token DECSTRING QUOTEDSTRING HEXSTRING STRING ANY
+ /* SPD management */
+%token SPDADD SPDDELETE SPDDUMP SPDFLUSH
+%token F_POLICY PL_REQUESTS
+%token F_AIFLAGS
+%token TAGGED
+
+%type <num> prefix protocol_spec upper_spec
+%type <num> ALG_ENC ALG_ENC_DESDERIV ALG_ENC_DES32IV ALG_ENC_OLD ALG_ENC_NOKEY
+%type <num> ALG_AUTH ALG_AUTH_NOKEY
+%type <num> ALG_COMP
+%type <num> PR_ESP PR_AH PR_IPCOMP PR_TCP
+%type <num> EXTENSION MODE
+%type <ulnum> DECSTRING
+%type <val> PL_REQUESTS portstr key_string
+%type <val> policy_requests
+%type <val> QUOTEDSTRING HEXSTRING STRING
+%type <val> F_AIFLAGS
+%type <val> upper_misc_spec policy_spec
+%type <res> ipaddr
+
+%%
+commands
+ : /*NOTHING*/
+ | commands command
+ {
+ free_buffer();
+ parse_init();
+ }
+ ;
+
+command
+ : add_command
+ | get_command
+ | delete_command
+ | deleteall_command
+ | flush_command
+ | dump_command
+ | spdadd_command
+ | spddelete_command
+ | spddump_command
+ | spdflush_command
+ ;
+ /* commands concerned with management, there is in tail of this file. */
+
+ /* add command */
+add_command
+ : ADD ipaddropts ipaddr ipaddr protocol_spec spi extension_spec algorithm_spec EOT
+ {
+ int status;
+
+ status = setkeymsg_add(SADB_ADD, $5, $3, $4);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* delete */
+delete_command
+ : DELETE ipaddropts ipaddr ipaddr protocol_spec spi extension_spec EOT
+ {
+ int status;
+
+ if ($3->ai_next || $4->ai_next) {
+ yyerror("multiple address specified");
+ return -1;
+ }
+ if (p_mode != IPSEC_MODE_ANY)
+ yyerror("WARNING: mode is obsolete");
+
+ status = setkeymsg_addr(SADB_DELETE, $5, $3, $4, 0);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* deleteall command */
+deleteall_command
+ : DELETEALL ipaddropts ipaddr ipaddr protocol_spec EOT
+ {
+ int status;
+
+ status = setkeymsg_addr(SADB_DELETE, $5, $3, $4, 1);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* get command */
+get_command
+ : GET ipaddropts ipaddr ipaddr protocol_spec spi extension_spec EOT
+ {
+ int status;
+
+ if (p_mode != IPSEC_MODE_ANY)
+ yyerror("WARNING: mode is obsolete");
+
+ status = setkeymsg_addr(SADB_GET, $5, $3, $4, 0);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+ /* flush */
+flush_command
+ : FLUSH protocol_spec EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_FLUSH, $2, sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+ /* dump */
+dump_command
+ : DUMP protocol_spec EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_DUMP, $2, sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+protocol_spec
+ : /*NOTHING*/
+ {
+ $$ = SADB_SATYPE_UNSPEC;
+ }
+ | PR_ESP
+ {
+ $$ = SADB_SATYPE_ESP;
+ if ($1 == 1)
+ p_ext |= SADB_X_EXT_OLD;
+ else
+ p_ext &= ~SADB_X_EXT_OLD;
+ }
+ | PR_AH
+ {
+ $$ = SADB_SATYPE_AH;
+ if ($1 == 1)
+ p_ext |= SADB_X_EXT_OLD;
+ else
+ p_ext &= ~SADB_X_EXT_OLD;
+ }
+ | PR_IPCOMP
+ {
+ $$ = SADB_X_SATYPE_IPCOMP;
+ }
+ | PR_TCP
+ {
+ $$ = SADB_X_SATYPE_TCPSIGNATURE;
+ }
+ ;
+
+spi
+ : DECSTRING { p_spi = $1; }
+ | HEXSTRING
+ {
+ char *ep;
+ unsigned long v;
+
+ ep = NULL;
+ v = strtoul($1.buf, &ep, 16);
+ if (!ep || *ep) {
+ yyerror("invalid SPI");
+ return -1;
+ }
+ if (v & ~0xffffffff) {
+ yyerror("SPI too big.");
+ return -1;
+ }
+
+ p_spi = v;
+ }
+ ;
+
+algorithm_spec
+ : esp_spec
+ | ah_spec
+ | ipcomp_spec
+ ;
+
+esp_spec
+ : F_ENC enc_alg F_AUTH auth_alg
+ | F_ENC enc_alg
+ ;
+
+ah_spec
+ : F_AUTH auth_alg
+ ;
+
+ipcomp_spec
+ : F_COMP ALG_COMP
+ {
+ if ($2 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $2;
+ }
+ | F_COMP ALG_COMP F_RAWCPI
+ {
+ if ($2 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $2;
+ p_ext |= SADB_X_EXT_RAWCPI;
+ }
+ ;
+
+enc_alg
+ : ALG_ENC_NOKEY {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+
+ p_key_enc_len = 0;
+ p_key_enc = NULL;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC key_string {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+
+ p_key_enc_len = $2.len;
+ p_key_enc = $2.buf;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC_OLD {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ yyerror("WARNING: obsolete algorithm");
+ p_alg_enc = $1;
+
+ p_key_enc_len = 0;
+ p_key_enc = NULL;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC_DESDERIV key_string
+ {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+ if (p_ext & SADB_X_EXT_OLD) {
+ yyerror("algorithm mismatched");
+ return -1;
+ }
+ p_ext |= SADB_X_EXT_DERIV;
+
+ p_key_enc_len = $2.len;
+ p_key_enc = $2.buf;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_ENC_DES32IV key_string
+ {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_enc = $1;
+ if (!(p_ext & SADB_X_EXT_OLD)) {
+ yyerror("algorithm mismatched");
+ return -1;
+ }
+ p_ext |= SADB_X_EXT_IV4B;
+
+ p_key_enc_len = $2.len;
+ p_key_enc = $2.buf;
+ if (ipsec_check_keylen(SADB_EXT_SUPPORTED_ENCRYPT,
+ p_alg_enc, PFKEY_UNUNIT64(p_key_enc_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ ;
+
+auth_alg
+ : ALG_AUTH key_string {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_auth = $1;
+
+ p_key_auth_len = $2.len;
+ p_key_auth = $2.buf;
+
+ if (p_alg_auth == SADB_X_AALG_TCP_MD5) {
+ if ((p_key_auth_len < 1) || (p_key_auth_len >
+ 80))
+ return -1;
+ } else if (ipsec_check_keylen(SADB_EXT_SUPPORTED_AUTH,
+ p_alg_auth, PFKEY_UNUNIT64(p_key_auth_len)) < 0) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+ }
+ | ALG_AUTH_NOKEY {
+ if ($1 < 0) {
+ yyerror("unsupported algorithm");
+ return -1;
+ }
+ p_alg_auth = $1;
+
+ p_key_auth_len = 0;
+ p_key_auth = NULL;
+ }
+ ;
+
+key_string
+ : QUOTEDSTRING
+ {
+ $$ = $1;
+ }
+ | HEXSTRING
+ {
+ caddr_t pp_key;
+ caddr_t bp;
+ caddr_t yp = $1.buf;
+ int l;
+
+ l = strlen(yp) % 2 + strlen(yp) / 2;
+ if ((pp_key = malloc(l)) == 0) {
+ yyerror("not enough core");
+ return -1;
+ }
+ memset(pp_key, 0, l);
+
+ bp = pp_key;
+ if (strlen(yp) % 2) {
+ *bp = ATOX(yp[0]);
+ yp++, bp++;
+ }
+ while (*yp) {
+ *bp = (ATOX(yp[0]) << 4) | ATOX(yp[1]);
+ yp += 2, bp++;
+ }
+
+ $$.len = l;
+ $$.buf = pp_key;
+ }
+ ;
+
+extension_spec
+ : /*NOTHING*/
+ | extension_spec extension
+ ;
+
+extension
+ : F_EXT EXTENSION { p_ext |= $2; }
+ | F_EXT NOCYCLICSEQ { p_ext &= ~SADB_X_EXT_CYCSEQ; }
+ | F_MODE MODE { p_mode = $2; }
+ | F_MODE ANY { p_mode = IPSEC_MODE_ANY; }
+ | F_REQID DECSTRING { p_reqid = $2; }
+ | F_REPLAY DECSTRING
+ {
+ if ((p_ext & SADB_X_EXT_OLD) != 0) {
+ yyerror("replay prevention cannot be used with "
+ "ah/esp-old");
+ return -1;
+ }
+ p_replay = $2;
+ }
+ | F_LIFETIME_HARD DECSTRING { p_lt_hard = $2; }
+ | F_LIFETIME_SOFT DECSTRING { p_lt_soft = $2; }
+ ;
+
+ /* definition about command for SPD management */
+ /* spdadd */
+spdadd_command
+ : SPDADD ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec policy_spec EOT
+ {
+ int status;
+ struct addrinfo *src, *dst;
+
+ /* fixed port fields if ulp is icmpv6 */
+ if ($10.buf != NULL) {
+ if ($9 != IPPROTO_ICMPV6)
+ return -1;
+ free($5.buf);
+ free($8.buf);
+ if (fix_portstr(&$10, &$5, &$8))
+ return -1;
+ }
+
+ src = parse_addr($3.buf, $5.buf);
+ dst = parse_addr($6.buf, $8.buf);
+ if (!src || !dst) {
+ /* yyerror is already called */
+ return -1;
+ }
+ if (src->ai_next || dst->ai_next) {
+ yyerror("multiple address specified");
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ return -1;
+ }
+
+ status = setkeymsg_spdaddr(SADB_X_SPDADD, $9, &$11,
+ src, $4, dst, $7);
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ if (status < 0)
+ return -1;
+ }
+ | SPDADD TAGGED QUOTEDSTRING policy_spec EOT
+ {
+ return -1;
+ }
+ ;
+
+spddelete_command
+ : SPDDELETE ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec policy_spec EOT
+ {
+ int status;
+ struct addrinfo *src, *dst;
+
+ /* fixed port fields if ulp is icmpv6 */
+ if ($10.buf != NULL) {
+ if ($9 != IPPROTO_ICMPV6)
+ return -1;
+ free($5.buf);
+ free($8.buf);
+ if (fix_portstr(&$10, &$5, &$8))
+ return -1;
+ }
+
+ src = parse_addr($3.buf, $5.buf);
+ dst = parse_addr($6.buf, $8.buf);
+ if (!src || !dst) {
+ /* yyerror is already called */
+ return -1;
+ }
+ if (src->ai_next || dst->ai_next) {
+ yyerror("multiple address specified");
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ return -1;
+ }
+
+ status = setkeymsg_spdaddr(SADB_X_SPDDELETE, $9, &$11,
+ src, $4, dst, $7);
+ freeaddrinfo(src);
+ freeaddrinfo(dst);
+ if (status < 0)
+ return -1;
+ }
+ ;
+
+spddump_command:
+ SPDDUMP EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_X_SPDDUMP, SADB_SATYPE_UNSPEC,
+ sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+spdflush_command:
+ SPDFLUSH EOT
+ {
+ struct sadb_msg msg;
+ setkeymsg0(&msg, SADB_X_SPDFLUSH, SADB_SATYPE_UNSPEC,
+ sizeof(msg));
+ sendkeymsg((char *)&msg, sizeof(msg));
+ }
+ ;
+
+ipaddropts
+ : /* nothing */
+ | ipaddropts ipaddropt
+ ;
+
+ipaddropt
+ : F_AIFLAGS
+ {
+ char *p;
+
+ for (p = $1.buf + 1; *p; p++)
+ switch (*p) {
+ case '4':
+ p_aifamily = AF_INET;
+ break;
+#ifdef INET6
+ case '6':
+ p_aifamily = AF_INET6;
+ break;
+#endif
+ case 'n':
+ p_aiflags = AI_NUMERICHOST;
+ break;
+ default:
+ yyerror("invalid flag");
+ return -1;
+ }
+ }
+ ;
+
+ipaddr
+ : STRING
+ {
+ $$ = parse_addr($1.buf, NULL);
+ if ($$ == NULL) {
+ /* yyerror already called by parse_addr */
+ return -1;
+ }
+ }
+ ;
+
+prefix
+ : /*NOTHING*/ { $$ = -1; }
+ | SLASH DECSTRING { $$ = $2; }
+ ;
+
+portstr
+ : /*NOTHING*/
+ {
+ $$.buf = strdup("0");
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ | BLCL ANY ELCL
+ {
+ $$.buf = strdup("0");
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ | BLCL DECSTRING ELCL
+ {
+ char buf[20];
+ snprintf(buf, sizeof(buf), "%lu", $2);
+ $$.buf = strdup(buf);
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ | BLCL STRING ELCL
+ {
+ $$ = $2;
+ }
+ ;
+
+upper_spec
+ : DECSTRING { $$ = $1; }
+ | ANY { $$ = IPSEC_ULPROTO_ANY; }
+ | PR_TCP { $$ = IPPROTO_TCP; }
+ | STRING
+ {
+ struct protoent *ent;
+
+ ent = getprotobyname($1.buf);
+ if (ent)
+ $$ = ent->p_proto;
+ else {
+ if (strcmp("icmp6", $1.buf) == 0) {
+ $$ = IPPROTO_ICMPV6;
+ } else if(strcmp("ip4", $1.buf) == 0) {
+ $$ = IPPROTO_IPV4;
+ } else {
+ yyerror("invalid upper layer protocol");
+ return -1;
+ }
+ }
+ endprotoent();
+ }
+ ;
+
+upper_misc_spec
+ : /*NOTHING*/
+ {
+ $$.buf = NULL;
+ $$.len = 0;
+ }
+ | STRING
+ {
+ $$.buf = strdup($1.buf);
+ if (!$$.buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ $$.len = strlen($$.buf);
+ }
+ ;
+
+policy_spec
+ : F_POLICY policy_requests
+ {
+ char *policy;
+
+ policy = ipsec_set_policy($2.buf, $2.len);
+ if (policy == NULL) {
+ yyerror(ipsec_strerror());
+ return -1;
+ }
+
+ $$.buf = policy;
+ $$.len = ipsec_get_policylen(policy);
+ }
+ ;
+
+policy_requests
+ : PL_REQUESTS { $$ = $1; }
+ ;
+
+%%
+
+int
+setkeymsg0(msg, type, satype, l)
+ struct sadb_msg *msg;
+ unsigned int type;
+ unsigned int satype;
+ size_t l;
+{
+
+ msg->sadb_msg_version = PF_KEY_V2;
+ msg->sadb_msg_type = type;
+ msg->sadb_msg_errno = 0;
+ msg->sadb_msg_satype = satype;
+ msg->sadb_msg_reserved = 0;
+ msg->sadb_msg_seq = 0;
+ msg->sadb_msg_pid = getpid();
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+ return 0;
+}
+
+/* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */
+static int
+setkeymsg_spdaddr(type, upper, policy, srcs, splen, dsts, dplen)
+ unsigned int type;
+ unsigned int upper;
+ vchar_t *policy;
+ struct addrinfo *srcs;
+ int splen;
+ struct addrinfo *dsts;
+ int dplen;
+{
+ struct sadb_msg *msg;
+ char buf[BUFSIZ];
+ int l, l0;
+ struct sadb_address m_addr;
+ struct addrinfo *s, *d;
+ int n;
+ int plen;
+ struct sockaddr *sa;
+ int salen;
+
+ msg = (struct sadb_msg *)buf;
+
+ if (!srcs || !dsts)
+ return -1;
+
+ /* fix up length afterwards */
+ setkeymsg0(msg, type, SADB_SATYPE_UNSPEC, 0);
+ l = sizeof(struct sadb_msg);
+
+ memcpy(buf + l, policy->buf, policy->len);
+ l += policy->len;
+
+ l0 = l;
+ n = 0;
+
+ /* do it for all src/dst pairs */
+ for (s = srcs; s; s = s->ai_next) {
+ for (d = dsts; d; d = d->ai_next) {
+ /* rewind pointer */
+ l = l0;
+
+ if (s->ai_addr->sa_family != d->ai_addr->sa_family)
+ continue;
+ switch (s->ai_addr->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /* set src */
+ sa = s->ai_addr;
+ salen = s->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = upper;
+ m_addr.sadb_address_prefixlen =
+ (splen >= 0 ? splen : plen);
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ /* set dst */
+ sa = d->ai_addr;
+ salen = d->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = upper;
+ m_addr.sadb_address_prefixlen =
+ (dplen >= 0 ? dplen : plen);
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+
+ sendkeymsg(buf, l);
+
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return -1;
+ else
+ return 0;
+}
+
+/* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */
+static int
+setkeymsg_addr(type, satype, srcs, dsts, no_spi)
+ unsigned int type;
+ unsigned int satype;
+ struct addrinfo *srcs;
+ struct addrinfo *dsts;
+ int no_spi;
+{
+ struct sadb_msg *msg;
+ char buf[BUFSIZ];
+ int l, l0, len;
+ struct sadb_sa m_sa;
+ struct sadb_x_sa2 m_sa2;
+ struct sadb_address m_addr;
+ struct addrinfo *s, *d;
+ int n;
+ int plen;
+ struct sockaddr *sa;
+ int salen;
+
+ msg = (struct sadb_msg *)buf;
+
+ if (!srcs || !dsts)
+ return -1;
+
+ /* fix up length afterwards */
+ setkeymsg0(msg, type, satype, 0);
+ l = sizeof(struct sadb_msg);
+
+ if (!no_spi) {
+ len = sizeof(struct sadb_sa);
+ m_sa.sadb_sa_len = PFKEY_UNIT64(len);
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(p_spi);
+ m_sa.sadb_sa_replay = p_replay;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = p_alg_auth;
+ m_sa.sadb_sa_encrypt = p_alg_enc;
+ m_sa.sadb_sa_flags = p_ext;
+
+ memcpy(buf + l, &m_sa, len);
+ l += len;
+
+ len = sizeof(struct sadb_x_sa2);
+ m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len);
+ m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ m_sa2.sadb_x_sa2_mode = p_mode;
+ m_sa2.sadb_x_sa2_reqid = p_reqid;
+
+ memcpy(buf + l, &m_sa2, len);
+ l += len;
+ }
+
+ l0 = l;
+ n = 0;
+
+ /* do it for all src/dst pairs */
+ for (s = srcs; s; s = s->ai_next) {
+ for (d = dsts; d; d = d->ai_next) {
+ /* rewind pointer */
+ l = l0;
+
+ if (s->ai_addr->sa_family != d->ai_addr->sa_family)
+ continue;
+ switch (s->ai_addr->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /* set src */
+ sa = s->ai_addr;
+ salen = s->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ /* set dst */
+ sa = d->ai_addr;
+ salen = d->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+
+ sendkeymsg(buf, l);
+
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return -1;
+ else
+ return 0;
+}
+
+/* XXX NO BUFFER OVERRUN CHECK! BAD BAD! */
+static int
+setkeymsg_add(type, satype, srcs, dsts)
+ unsigned int type;
+ unsigned int satype;
+ struct addrinfo *srcs;
+ struct addrinfo *dsts;
+{
+ struct sadb_msg *msg;
+ char buf[BUFSIZ];
+ int l, l0, len;
+ struct sadb_sa m_sa;
+ struct sadb_x_sa2 m_sa2;
+ struct sadb_address m_addr;
+ struct addrinfo *s, *d;
+ int n;
+ int plen;
+ struct sockaddr *sa;
+ int salen;
+
+ msg = (struct sadb_msg *)buf;
+
+ if (!srcs || !dsts)
+ return -1;
+
+ /* fix up length afterwards */
+ setkeymsg0(msg, type, satype, 0);
+ l = sizeof(struct sadb_msg);
+
+ /* set encryption algorithm, if present. */
+ if (satype != SADB_X_SATYPE_IPCOMP && p_key_enc) {
+ struct sadb_key m_key;
+
+ m_key.sadb_key_len =
+ PFKEY_UNIT64(sizeof(m_key)
+ + PFKEY_ALIGN8(p_key_enc_len));
+ m_key.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ m_key.sadb_key_bits = p_key_enc_len * 8;
+ m_key.sadb_key_reserved = 0;
+
+ setvarbuf(buf, &l,
+ (struct sadb_ext *)&m_key, sizeof(m_key),
+ (caddr_t)p_key_enc, p_key_enc_len);
+ }
+
+ /* set authentication algorithm, if present. */
+ if (p_key_auth) {
+ struct sadb_key m_key;
+
+ m_key.sadb_key_len =
+ PFKEY_UNIT64(sizeof(m_key)
+ + PFKEY_ALIGN8(p_key_auth_len));
+ m_key.sadb_key_exttype = SADB_EXT_KEY_AUTH;
+ m_key.sadb_key_bits = p_key_auth_len * 8;
+ m_key.sadb_key_reserved = 0;
+
+ setvarbuf(buf, &l,
+ (struct sadb_ext *)&m_key, sizeof(m_key),
+ (caddr_t)p_key_auth, p_key_auth_len);
+ }
+
+ /* set lifetime for HARD */
+ if (p_lt_hard != 0) {
+ struct sadb_lifetime m_lt;
+ u_int slen = sizeof(struct sadb_lifetime);
+
+ m_lt.sadb_lifetime_len = PFKEY_UNIT64(slen);
+ m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+ m_lt.sadb_lifetime_allocations = 0;
+ m_lt.sadb_lifetime_bytes = 0;
+ m_lt.sadb_lifetime_addtime = p_lt_hard;
+ m_lt.sadb_lifetime_usetime = 0;
+
+ memcpy(buf + l, &m_lt, slen);
+ l += len;
+ }
+
+ /* set lifetime for SOFT */
+ if (p_lt_soft != 0) {
+ struct sadb_lifetime m_lt;
+ u_int slen = sizeof(struct sadb_lifetime);
+
+ m_lt.sadb_lifetime_len = PFKEY_UNIT64(slen);
+ m_lt.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
+ m_lt.sadb_lifetime_allocations = 0;
+ m_lt.sadb_lifetime_bytes = 0;
+ m_lt.sadb_lifetime_addtime = p_lt_soft;
+ m_lt.sadb_lifetime_usetime = 0;
+
+ memcpy(buf + l, &m_lt, slen);
+ l += len;
+ }
+
+ len = sizeof(struct sadb_sa);
+ m_sa.sadb_sa_len = PFKEY_UNIT64(len);
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(p_spi);
+ m_sa.sadb_sa_replay = p_replay;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = p_alg_auth;
+ m_sa.sadb_sa_encrypt = p_alg_enc;
+ m_sa.sadb_sa_flags = p_ext;
+
+ memcpy(buf + l, &m_sa, len);
+ l += len;
+
+ len = sizeof(struct sadb_x_sa2);
+ m_sa2.sadb_x_sa2_len = PFKEY_UNIT64(len);
+ m_sa2.sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+ m_sa2.sadb_x_sa2_mode = p_mode;
+ m_sa2.sadb_x_sa2_reqid = p_reqid;
+
+ memcpy(buf + l, &m_sa2, len);
+ l += len;
+
+ l0 = l;
+ n = 0;
+
+ /* do it for all src/dst pairs */
+ for (s = srcs; s; s = s->ai_next) {
+ for (d = dsts; d; d = d->ai_next) {
+ /* rewind pointer */
+ l = l0;
+
+ if (s->ai_addr->sa_family != d->ai_addr->sa_family)
+ continue;
+ switch (s->ai_addr->sa_family) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /* set src */
+ sa = s->ai_addr;
+ salen = s->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ /* set dst */
+ sa = d->ai_addr;
+ salen = d->ai_addr->sa_len;
+ m_addr.sadb_address_len = PFKEY_UNIT64(sizeof(m_addr) +
+ PFKEY_ALIGN8(salen));
+ m_addr.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+ m_addr.sadb_address_proto = IPSEC_ULPROTO_ANY;
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ setvarbuf(buf, &l, (struct sadb_ext *)&m_addr,
+ sizeof(m_addr), (caddr_t)sa, salen);
+
+ msg->sadb_msg_len = PFKEY_UNIT64(l);
+
+ sendkeymsg(buf, l);
+
+ n++;
+ }
+ }
+
+ if (n == 0)
+ return -1;
+ else
+ return 0;
+}
+
+static struct addrinfo *
+parse_addr(host, port)
+ char *host;
+ char *port;
+{
+ struct addrinfo hints, *res = NULL;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = p_aifamily;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_protocol = IPPROTO_UDP; /*dummy*/
+ hints.ai_flags = p_aiflags;
+ error = getaddrinfo(host, port, &hints, &res);
+ if (error != 0) {
+ yyerror(gai_strerror(error));
+ return NULL;
+ }
+ return res;
+}
+
+static int
+fix_portstr(spec, sport, dport)
+ vchar_t *spec, *sport, *dport;
+{
+ char *p, *p2;
+ u_int l;
+
+ l = 0;
+ for (p = spec->buf; *p != ',' && *p != '\0' && l < spec->len; p++, l++)
+ ;
+ if (*p == '\0') {
+ p2 = "0";
+ } else {
+ if (*p == ',') {
+ *p = '\0';
+ p2 = ++p;
+ }
+ for (p = p2; *p != '\0' && l < spec->len; p++, l++)
+ ;
+ if (*p != '\0' || *p2 == '\0') {
+ yyerror("invalid an upper layer protocol spec");
+ return -1;
+ }
+ }
+
+ sport->buf = strdup(spec->buf);
+ if (!sport->buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ sport->len = strlen(sport->buf);
+ dport->buf = strdup(p2);
+ if (!dport->buf) {
+ yyerror("insufficient memory");
+ return -1;
+ }
+ dport->len = strlen(dport->buf);
+
+ return 0;
+}
+
+static int
+setvarbuf(buf, off, ebuf, elen, vbuf, vlen)
+ char *buf;
+ int *off;
+ struct sadb_ext *ebuf;
+ int elen;
+ caddr_t vbuf;
+ int vlen;
+{
+ memset(buf + *off, 0, PFKEY_UNUNIT64(ebuf->sadb_ext_len));
+ memcpy(buf + *off, (caddr_t)ebuf, elen);
+ memcpy(buf + *off + elen, vbuf, vlen);
+ (*off) += PFKEY_ALIGN8(elen + vlen);
+
+ return 0;
+}
+
+void
+parse_init()
+{
+ p_spi = 0;
+
+ p_ext = SADB_X_EXT_CYCSEQ;
+ p_alg_enc = SADB_EALG_NONE;
+ p_alg_auth = SADB_AALG_NONE;
+ p_mode = IPSEC_MODE_ANY;
+ p_reqid = 0;
+ p_replay = 0;
+ p_key_enc_len = p_key_auth_len = 0;
+ p_key_enc = p_key_auth = 0;
+ p_lt_hard = p_lt_soft = 0;
+
+ p_aiflags = 0;
+ p_aifamily = PF_UNSPEC;
+
+ return;
+}
+
+void
+free_buffer()
+{
+ /* we got tons of memory leaks in the parser anyways, leave them */
+
+ return;
+}
diff --git a/usr.sbin/setkey/sample.cf b/usr.sbin/setkey/sample.cf
new file mode 100644
index 0000000..c534fa1
--- /dev/null
+++ b/usr.sbin/setkey/sample.cf
@@ -0,0 +1,219 @@
+# Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the project nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+# There are sample scripts for IPsec configuration by manual keying.
+# A security association is uniquely identified by a triple consisting
+# of a Security Parameter Index (SPI), an IP Destination Address, and a
+# security protocol (AH or ESP) identifier. You must take care of these
+# parameters when you configure by manual keying.
+
+# ESP transport mode is recommended for TCP port number 110 between
+# Host-A and Host-B. Encryption algorithm is blowfish-cbc whose key
+# is "kamekame", and authentication algorithm is hmac-sha1 whose key
+# is "this is the test key".
+#
+# ============ ESP ============
+# | |
+# Host-A Host-B
+# fec0::10 -------------------- fec0::11
+#
+# At Host-A and Host-B,
+spdadd fec0::10[any] fec0::11[110] tcp -P out ipsec
+ esp/transport//use ;
+spdadd fec0::11[110] fec0::10[any] tcp -P in ipsec
+ esp/transport//use ;
+add fec0::10 fec0::11 esp 0x10001
+ -m transport
+ -E blowfish-cbc "kamekame"
+ -A hmac-sha1 "this is the test key" ;
+add fec0::11 fec0::10 esp 0x10002
+ -m transport
+ -E blowfish-cbc "kamekame"
+ -A hmac-sha1 "this is the test key" ;
+
+# "[any]" is wildcard of port number. Note that "[0]" is the number of
+# zero in port number.
+
+# Security protocol is old AH tunnel mode, i.e. RFC1826, with keyed-md5
+# whose key is "this is the test" as authentication algorithm.
+# That protocol takes place between Gateway-A and Gateway-B.
+#
+# ======= AH =======
+# | |
+# Network-A Gateway-A Gateway-B Network-B
+# 10.0.1.0/24 ---- 172.16.0.1 ----- 172.16.0.2 ---- 10.0.2.0/24
+#
+# At Gateway-A:
+spdadd 10.0.1.0/24 10.0.2.0/24 any -P out ipsec
+ ah/tunnel/172.16.0.1-172.16.0.2/require ;
+spdadd 10.0.2.0/24 10.0.1.0/24 any -P in ipsec
+ ah/tunnel/172.16.0.2-172.16.0.1/require ;
+add 172.16.0.1 172.16.0.2 ah-old 0x10003
+ -m any
+ -A keyed-md5 "this is the test" ;
+add 172.16.0.2 172.16.0.1 ah-old 0x10004
+ -m any
+ -A keyed-md5 "this is the test" ;
+
+# If port number field is omitted such above then "[any]" is employed.
+# -m specifies the mode of SA to be used. "-m any" means wildcard of
+# mode of security protocol. You can use this SAs for both tunnel and
+# transport mode.
+
+# At Gateway-B. Attention to the selector and peer's IP address for tunnel.
+spdadd 10.0.2.0/24 10.0.1.0/24 any -P out ipsec
+ ah/tunnel/172.16.0.2-172.16.0.1/require ;
+spdadd 10.0.1.0/24 10.0.2.0/24 any -P in ipsec
+ ah/tunnel/172.16.0.1-172.16.0.2/require ;
+add 172.16.0.1 172.16.0.2 ah-old 0x10003
+ -m tunnel
+ -A keyed-md5 "this is the test" ;
+add 172.16.0.2 172.16.0.1 ah-old 0x10004
+ -m tunnel
+ -A keyed-md5 "this is the test" ;
+
+# AH transport mode followed by ESP tunnel mode is required between
+# Gateway-A and Gateway-B.
+# Encryption algorithm is 3des-cbc, and authentication algorithm for ESP
+# is hmac-sha1. Authentication algorithm for AH is hmac-md5.
+#
+# ========== AH =========
+# | ======= ESP ===== |
+# | | | |
+# Network-A Gateway-A Gateway-B Network-B
+# fec0:0:0:1::/64 --- fec0:0:0:1::1 ---- fec0:0:0:2::1 --- fec0:0:0:2::/64
+#
+# At Gateway-A:
+spdadd fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out ipsec
+ esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require
+ ah/transport//require ;
+spdadd fec0:0:0:2::/64 fec0:0:0:1::/64 any -P in ipsec
+ esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require
+ ah/transport//require ;
+add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10001
+ -m tunnel
+ -E 3des-cbc "kamekame12341234kame1234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:1::1 fec0:0:0:2::1 ah 0x10001
+ -m transport
+ -A hmac-md5 "this is the test" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10001
+ -m tunnel
+ -E 3des-cbc "kamekame12341234kame1234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 ah 0x10001
+ -m transport
+ -A hmac-md5 "this is the test" ;
+
+# ESP tunnel mode is required between Host-A and Gateway-A.
+# Encryption algorithm is cast128-cbc, and authentication algorithm
+# for ESP is hmac-sha1.
+# ESP transport mode is recommended between Host-A and Host-B.
+# Encryption algorithm is rc5-cbc, and authentication algorithm
+# for ESP is hmac-md5.
+#
+# ================== ESP =================
+# | ======= ESP ======= |
+# | | | |
+# Host-A Gateway-A Host-B
+# fec0:0:0:1::1 ---- fec0:0:0:2::1 ---- fec0:0:0:2::2
+#
+# At Host-A:
+spdadd fec0:0:0:1::1[any] fec0:0:0:2::2[80] tcp -P out ipsec
+ esp/transport//use
+ esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require ;
+spdadd fec0:0:0:2::1[80] fec0:0:0:1::1[any] tcp -P in ipsec
+ esp/transport//use
+ esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require ;
+add fec0:0:0:1::1 fec0:0:0:2::2 esp 0x10001
+ -m transport
+ -E cast128-cbc "12341234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10002
+ -E rc5-cbc "kamekame"
+ -A hmac-md5 "this is the test" ;
+add fec0:0:0:2::2 fec0:0:0:1::1 esp 0x10003
+ -m transport
+ -E cast128-cbc "12341234"
+ -A hmac-sha1 "this is the test key" ;
+add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10004
+ -E rc5-cbc "kamekame"
+ -A hmac-md5 "this is the test" ;
+
+# By "get" command, you can get a entry of either SP or SA.
+get fec0:0:0:1::1 fec0:0:0:2::2 ah 0x10004 ;
+
+# Also delete command, you can delete a entry of either SP or SA.
+spddelete fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out;
+delete fec0:0:0:1::1 fec0:0:0:2::2 ah 0x10004 ;
+
+# By dump command, you can dump all entry of either SP or SA.
+dump ;
+spddump ;
+dump esp ;
+flush esp ;
+
+# By flush command, you can flush all entry of either SP or SA.
+flush ;
+spdflush ;
+
+# "flush" and "dump" commands can specify a security protocol.
+dump esp ;
+flush ah ;
+
+# XXX
+add ::1 ::1 esp 10001 -m transport -E null ;
+add ::1 ::1 esp 10002 -m transport -E des-deriv "12341234" ;
+add ::1 ::1 esp-old 10003 -m transport -E des-32iv "12341234" ;
+add ::1 ::1 esp 10004 -m transport -E null -A null ;
+add ::1 ::1 esp 10005 -m transport -E null -A hmac-md5 "1234123412341234" ;
+add ::1 ::1 esp 10006 -m tunnel -E null -A hmac-sha1 "12341234123412341234" ;
+add ::1 ::1 esp 10007 -m transport -E null -A keyed-md5 "1234123412341234" ;
+add ::1 ::1 esp 10008 -m any -E null -A keyed-sha1 "12341234123412341234" ;
+add ::1 ::1 esp 10009 -m transport -E des-cbc "testtest" ;
+add ::1 ::1 esp 10010 -m transport -E 3des-cbc "testtest12341234testtest" ;
+add ::1 ::1 esp 10011 -m tunnel -E cast128-cbc "testtest1234" ;
+add ::1 ::1 esp 10012 -m tunnel -E blowfish-cbc "testtest1234" ;
+add ::1 ::1 esp 10013 -m tunnel -E rc5-cbc "testtest1234" ;
+add ::1 ::1 esp 10014 -m any -E rc5-cbc "testtest1234" ;
+add ::1 ::1 esp 10015 -m transport -f zero-pad -E null ;
+add ::1 ::1 esp 10016 -m tunnel -f random-pad -r 8 -lh 100 -ls 80 -E null ;
+add ::1 ::1 esp 10017 -m transport -f seq-pad -f nocyclic-seq -E null ;
+add ::1 ::1 esp 10018 -m transport -E null ;
+#add ::1 ::1 ah 20000 -m transport -A null ;
+add ::1 ::1 ah 20001 -m any -A hmac-md5 "1234123412341234";
+add ::1 ::1 ah 20002 -m tunnel -A hmac-sha1 "12341234123412341234";
+add ::1 ::1 ah 20003 -m transport -A keyed-md5 "1234123412341234";
+add ::1 ::1 ah-old 20004 -m transport -A keyed-md5 "1234123412341234";
+add ::1 ::1 ah 20005 -m transport -A keyed-sha1 "12341234123412341234";
+#add ::1 ::1 ipcomp 30000 -C oui ;
+add ::1 ::1 ipcomp 30001 -C deflate ;
+#add ::1 ::1 ipcomp 30002 -C lzs ;
+
+# enjoy.
diff --git a/usr.sbin/setkey/scriptdump.pl b/usr.sbin/setkey/scriptdump.pl
new file mode 100644
index 0000000..a1d8adb
--- /dev/null
+++ b/usr.sbin/setkey/scriptdump.pl
@@ -0,0 +1,56 @@
+#! @LOCALPREFIX@/bin/perl
+# $FreeBSD$
+
+if ($< != 0) {
+ print STDERR "must be root to invoke this\n";
+ exit 1;
+}
+
+$mode = 'add';
+while ($i = shift @ARGV) {
+ if ($i eq '-d') {
+ $mode = 'delete';
+ } else {
+ print STDERR "usage: scriptdump [-d]\n";
+ exit 1;
+ }
+}
+
+open(IN, "setkey -D |") || die;
+foreach $_ (<IN>) {
+ if (/^[^\t]/) {
+ ($src, $dst) = split(/\s+/, $_);
+ } elsif (/^\t(esp|ah) mode=(\S+) spi=(\d+).*reqid=(\d+)/) {
+ ($proto, $ipsecmode, $spi, $reqid) = ($1, $2, $3, $4);
+ } elsif (/^\tE: (\S+) (.*)/) {
+ $ealgo = $1;
+ $ekey = $2;
+ $ekey =~ s/\s//g;
+ $ekey =~ s/^/0x/g;
+ } elsif (/^\tA: (\S+) (.*)/) {
+ $aalgo = $1;
+ $akey = $2;
+ $akey =~ s/\s//g;
+ $akey =~ s/^/0x/g;
+ } elsif (/^\tseq=(0x\d+) replay=(\d+) flags=(0x\d+) state=/) {
+ print "$mode $src $dst $proto $spi";
+ $replay = $2;
+ print " -u $reqid" if $reqid;
+ if ($mode eq 'add') {
+ print " -m $ipsecmode -r $replay" if $replay;
+ if ($proto eq 'esp') {
+ print " -E $ealgo $ekey" if $ealgo;
+ print " -A $aalgo $akey" if $aalgo;
+ } elsif ($proto eq 'ah') {
+ print " -A $aalgo $akey" if $aalgo;
+ }
+ }
+ print ";\n";
+
+ $src = $dst = $upper = $proxy = '';
+ $ealgo = $ekey = $aalgo = $akey = '';
+ }
+}
+close(IN);
+
+exit 0;
diff --git a/usr.sbin/setkey/setkey.8 b/usr.sbin/setkey/setkey.8
new file mode 100644
index 0000000..91aba57
--- /dev/null
+++ b/usr.sbin/setkey/setkey.8
@@ -0,0 +1,692 @@
+.\" $KAME: setkey.8,v 1.89 2003/09/07 22:17:41 itojun Exp $
+.\" $FreeBSD$
+.\"
+.\" 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.
+.\"
+.Dd November 20, 2000
+.Dt SETKEY 8
+.Os
+.\"
+.Sh NAME
+.Nm setkey
+.Nd "manually manipulate the IPsec SA/SP database"
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Fl c
+.Nm
+.Op Fl v
+.Fl f Ar filename
+.Nm
+.Op Fl aPlv
+.Fl D
+.Nm
+.Op Fl Pv
+.Fl F
+.Nm
+.Op Fl h
+.Fl x
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility adds, updates, dumps, or flushes
+Security Association Database (SAD) entries
+as well as Security Policy Database (SPD) entries in the kernel.
+.Pp
+The
+.Nm
+utility takes a series of operations from the standard input
+(if invoked with
+.Fl c )
+or the file named
+.Ar filename
+(if invoked with
+.Fl f Ar filename ) .
+.Bl -tag -width indent
+.It Fl D
+Dump the SAD entries.
+If with
+.Fl P ,
+the SPD entries are dumped.
+.It Fl F
+Flush the SAD entries.
+If with
+.Fl P ,
+the SPD entries are flushed.
+.It Fl a
+The
+.Nm
+utility
+usually does not display dead SAD entries with
+.Fl D .
+If with
+.Fl a ,
+the dead SAD entries will be displayed as well.
+A dead SAD entry means that
+it has been expired but remains in the system
+because it is referenced by some SPD entries.
+.It Fl h
+Add hexadecimal dump on
+.Fl x
+mode.
+.It Fl l
+Loop forever with short output on
+.Fl D .
+.It Fl v
+Be verbose.
+The program will dump messages exchanged on
+.Dv PF_KEY
+socket, including messages sent from other processes to the kernel.
+.It Fl x
+Loop forever and dump all the messages transmitted to
+.Dv PF_KEY
+socket.
+.Fl xx
+makes each timestamps unformatted.
+.El
+.Ss Configuration syntax
+With
+.Fl c
+or
+.Fl f
+on the command line,
+.Nm
+accepts the following configuration syntax.
+Lines starting with hash signs
+.Pq Ql #
+are treated as comment lines.
+.Bl -tag -width indent
+.It Xo
+.Li add
+.Op Fl 46n
+.Ar src Ar dst Ar protocol Ar spi
+.Op Ar extensions
+.Ar algorithm ...
+.Li ;
+.Xc
+Add an SAD entry.
+.Li add
+can fail with multiple reasons,
+including when the key length does not match the specified algorithm.
+.\"
+.It Xo
+.Li get
+.Op Fl 46n
+.Ar src Ar dst Ar protocol Ar spi
+.Li ;
+.Xc
+Show an SAD entry.
+.\"
+.It Xo
+.Li delete
+.Op Fl 46n
+.Ar src Ar dst Ar protocol Ar spi
+.Li ;
+.Xc
+Remove an SAD entry.
+.\"
+.It Xo
+.Li deleteall
+.Op Fl 46n
+.Ar src Ar dst Ar protocol
+.Li ;
+.Xc
+Remove all SAD entries that match the specification.
+.\"
+.It Xo
+.Li flush
+.Op Ar protocol
+.Li ;
+.Xc
+Clear all SAD entries matched by the options.
+.Fl F
+on the command line achieves the same functionality.
+.\"
+.It Xo
+.Li dump
+.Op Ar protocol
+.Li ;
+.Xc
+Dumps all SAD entries matched by the options.
+.Fl D
+on the command line achieves the same functionality.
+.\"
+.It Xo
+.Li spdadd
+.Op Fl 46n
+.Ar src_range Ar dst_range Ar upperspec Ar policy
+.Li ;
+.Xc
+Add an SPD entry.
+.\"
+.It Xo
+.Li spddelete
+.Op Fl 46n
+.Ar src_range Ar dst_range Ar upperspec Fl P Ar direction
+.Li ;
+.Xc
+Delete an SPD entry.
+.\"
+.It Xo
+.Li spdflush
+.Li ;
+.Xc
+Clear all SPD entries.
+.Fl FP
+on the command line achieves the same functionality.
+.\"
+.It Xo
+.Li spddump
+.Li ;
+.Xc
+Dumps all SPD entries.
+.Fl DP
+on the command line achieves the same functionality.
+.El
+.\"
+.Pp
+Meta-arguments are as follows:
+.Pp
+.Bl -tag -compact -width indent
+.It Ar src
+.It Ar dst
+Source/destination of the secure communication is specified as
+IPv4/v6 address.
+The
+.Nm
+utility
+can resolve a FQDN into numeric addresses.
+If the FQDN resolves into multiple addresses,
+.Nm
+will install multiple SAD/SPD entries into the kernel
+by trying all possible combinations.
+.Fl 4 ,
+.Fl 6
+and
+.Fl n
+restricts the address resolution of FQDN in certain ways.
+.Fl 4
+and
+.Fl 6
+restrict results into IPv4/v6 addresses only, respectively.
+.Fl n
+avoids FQDN resolution and requires addresses to be numeric addresses.
+.\"
+.Pp
+.It Ar protocol
+.Ar protocol
+is one of following:
+.Bl -tag -width Fl -compact
+.It Li esp
+ESP based on rfc2406
+.It Li esp-old
+ESP based on rfc1827
+.It Li ah
+AH based on rfc2402
+.It Li ah-old
+AH based on rfc1826
+.It Li ipcomp
+IPComp
+.It Li tcp
+TCP-MD5 based on rfc2385
+.El
+.\"
+.Pp
+.It Ar spi
+Security Parameter Index
+(SPI)
+for the SAD and the SPD.
+.Ar spi
+must be a decimal number, or a hexadecimal number with
+.Ql 0x
+prefix.
+SPI values between 0 and 255 are reserved for future use by IANA
+and they cannot be used.
+TCP-MD5 associations must use 0x1000 and therefore only have per-host
+granularity at this time.
+.\"
+.Pp
+.It Ar extensions
+take some of the following:
+.Bl -tag -width Fl -compact
+.\"
+.It Fl m Ar mode
+Specify a security protocol mode for use.
+.Ar mode
+is one of following:
+.Li transport , tunnel
+or
+.Li any .
+The default value is
+.Li any .
+.\"
+.It Fl r Ar size
+Specify window size of bytes for replay prevention.
+.Ar size
+must be decimal number in 32-bit word.
+If
+.Ar size
+is zero or not specified, replay check does not take place.
+.\"
+.It Fl u Ar id
+Specify the identifier of the policy entry in SPD.
+See
+.Ar policy .
+.\"
+.It Fl f Ar pad_option
+defines the content of the ESP padding.
+.Ar pad_option
+is one of following:
+.Bl -tag -width random-pad -compact
+.It Li zero-pad
+All of the padding are zero.
+.It Li random-pad
+A series of randomized values are set.
+.It Li seq-pad
+A series of sequential increasing numbers started from 1 are set.
+.El
+.\"
+.It Fl f Li nocyclic-seq
+Do not allow cyclic sequence number.
+.\"
+.It Fl lh Ar time
+.It Fl ls Ar time
+Specify hard/soft life time duration of the SA.
+.El
+.\"
+.Pp
+.It Ar algorithm
+.Bl -tag -width Fl -compact
+.It Fl E Ar ealgo Ar key
+Specify an encryption algorithm
+.Ar ealgo
+for ESP.
+.It Xo
+.Fl E Ar ealgo Ar key
+.Fl A Ar aalgo Ar key
+.Xc
+Specify a encryption algorithm
+.Ar ealgo ,
+as well as a payload authentication algorithm
+.Ar aalgo ,
+for ESP.
+.It Fl A Ar aalgo Ar key
+Specify an authentication algorithm for AH.
+.It Fl C Ar calgo Op Fl R
+Specify a compression algorithm for IPComp.
+If
+.Fl R
+is specified,
+.Ar spi
+field value will be used as the IPComp CPI
+(compression parameter index)
+on wire as is.
+If
+.Fl R
+is not specified,
+the kernel will use well-known CPI on wire, and
+.Ar spi
+field will be used only as an index for kernel internal usage.
+.El
+.Pp
+.Ar key
+must be double-quoted character string, or a series of hexadecimal digits
+preceded by
+.Ql 0x .
+.Pp
+Possible values for
+.Ar ealgo ,
+.Ar aalgo
+and
+.Ar calgo
+are specified in separate section.
+.\"
+.Pp
+.It Ar src_range
+.It Ar dst_range
+These are selections of the secure communication specified as
+IPv4/v6 address or IPv4/v6 address range, and it may accompany
+TCP/UDP port specification.
+This takes the following form:
+.Bd -literal -offset
+.Ar address
+.Ar address/prefixlen
+.Ar address[port]
+.Ar address/prefixlen[port]
+.Ed
+.Pp
+.Ar prefixlen
+and
+.Ar port
+must be decimal number.
+The square bracket around
+.Ar port
+is really necessary.
+They are not manpage metacharacters.
+For FQDN resolution, the rules applicable to
+.Ar src
+and
+.Ar dst
+apply here as well.
+.\"
+.Pp
+.It Ar upperspec
+Upper-layer protocol to be used.
+You can use one of words in
+.Pa /etc/protocols
+as
+.Ar upperspec .
+Or
+.Li icmp6 ,
+.Li ip4 ,
+and
+.Li any
+can be specified.
+.Li any
+stands for
+.Dq any protocol .
+Also you can use the protocol number.
+You can specify a type and/or a code of ICMPv6 when
+upper-layer protocol is ICMPv6.
+The specification can be placed after
+.Li icmp6 .
+A type is separated with a code by single comma.
+A code must be specified anytime.
+When a zero is specified, the kernel deals with it as a wildcard.
+Note that the kernel cannot distinguish a wildcard from that a type
+of ICMPv6 is zero.
+For example, the following means the policy does not require IPsec
+for any inbound Neighbor Solicitation:
+.Pp
+.Dl "spdadd ::/0 ::/0 icmp6 135,0 -P in none;"
+.Pp
+NOTE:
+.Ar upperspec
+does not work against forwarding case at this moment,
+as it requires extra reassembly at forwarding node
+(not implemented at this moment).
+We have many protocols in
+.Pa /etc/protocols ,
+but protocols except of TCP, UDP and ICMP may not be suitable to use with IPsec.
+You have to consider and be careful to use them.
+.\"
+.Pp
+.It Ar policy
+.Ar policy
+is the one of the following three formats:
+.Bd -ragged -offset indent
+.It Fl P Ar direction Li discard
+.It Fl P Ar direction Li none
+.It Xo Fl P Ar direction Li ipsec
+.Ar protocol/mode/src-dst/level Op ...
+.Xc
+.Ed
+.Pp
+You must specify the direction of its policy as
+.Ar direction .
+Either
+.Li out
+or
+.Li in
+are used.
+.Li discard
+means the packet matching indexes will be discarded.
+.Li none
+means that IPsec operation will not take place onto the packet.
+.Li ipsec
+means that IPsec operation will take place onto the packet.
+The part of
+.Ar protocol/mode/src-dst/level
+specifies the rule how to process the packet.
+Either
+.Li ah ,
+.Li esp
+or
+.Li ipcomp
+is to be set as
+.Ar protocol .
+.Ar mode
+is either
+.Li transport
+or
+.Li tunnel .
+If
+.Ar mode
+is
+.Li tunnel ,
+you must specify the end-points addresses of the SA as
+.Ar src
+and
+.Ar dst
+with
+.Sq -
+between these addresses which is used to specify the SA to use.
+If
+.Ar mode
+is
+.Li transport ,
+both
+.Ar src
+and
+.Ar dst
+can be omitted.
+.Ar level
+is to be one of the following:
+.Li default , use , require
+or
+.Li unique .
+If the SA is not available in every level, the kernel will request
+getting SA to the key exchange daemon.
+.Li default
+means the kernel consults to the system wide default against protocol you
+specified, e.g.,
+.Li esp_trans_deflev
+sysctl variable, when the kernel processes the packet.
+.Li use
+means that the kernel use a SA if it is available,
+otherwise the kernel keeps normal operation.
+.Li require
+means SA is required whenever the kernel sends a packet matched
+with the policy.
+.Li unique
+is the same to require.
+In addition, it allows the policy to bind with the unique out-bound SA.
+You just specify the policy level
+.Li unique ,
+.Xr racoon 8
+will configure the SA for the policy.
+If you configure the SA by manual keying for that policy,
+you can put the decimal number as the policy identifier after
+.Li unique
+separated by colon
+.Ql :\&
+like the following;
+.Li unique:number .
+In order to bind this policy to the SA,
+.Li number
+must be between 1 and 32767.
+It corresponds to
+.Ar extensions Fl u
+of the manual SA configuration.
+When you want to use SA bundle, you can define multiple rules.
+For example, if an IP header was followed by AH header followed by ESP header
+followed by an upper layer protocol header, the rule
+would be:
+.Dl esp/transport//require ah/transport//require ;
+The rule order is very important.
+.Pp
+Note that
+.Dq Li discard
+and
+.Dq Li none
+are not in the syntax described in
+.Xr ipsec_set_policy 3 .
+There are little differences in the syntax.
+See
+.Xr ipsec_set_policy 3
+for detail.
+.Pp
+.El
+.Pp
+.\"
+.Sh ALGORITHMS
+The following list shows the supported algorithms.
+.Sy protocol
+and
+.Sy algorithm
+are almost orthogonal.
+Followings are the list of authentication algorithms that can be used as
+.Ar aalgo
+in
+.Fl A Ar aalgo
+of
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm keylen (bits) comment
+hmac-md5 128 ah: rfc2403
+ 128 ah-old: rfc2085
+hmac-sha1 160 ah: rfc2404
+ 160 ah-old: 128bit ICV (no document)
+keyed-md5 128 ah: 96bit ICV (no document)
+ 128 ah-old: rfc1828
+keyed-sha1 160 ah: 96bit ICV (no document)
+ 160 ah-old: 128bit ICV (no document)
+null 0 to 2048 for debugging
+hmac-sha2-256 256 ah: 96bit ICV
+ (draft-ietf-ipsec-ciph-sha-256-00)
+ 256 ah-old: 128bit ICV (no document)
+hmac-sha2-384 384 ah: 96bit ICV (no document)
+ 384 ah-old: 128bit ICV (no document)
+hmac-sha2-512 512 ah: 96bit ICV (no document)
+ 512 ah-old: 128bit ICV (no document)
+hmac-ripemd160 160 ah: 96bit ICV (RFC2857)
+ ah-old: 128bit ICV (no document)
+aes-xcbc-mac 128 ah: 96bit ICV (RFC3566)
+ 128 ah-old: 128bit ICV (no document)
+tcp-md5 8 to 640 tcp: rfc2385
+.Ed
+.Pp
+Followings are the list of encryption algorithms that can be used as
+.Ar ealgo
+in
+.Fl E Ar ealgo
+of
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm keylen (bits) comment
+des-cbc 64 esp-old: rfc1829, esp: rfc2405
+3des-cbc 192 rfc2451
+null 0 to 2048 rfc2410
+blowfish-cbc 40 to 448 rfc2451
+cast128-cbc 40 to 128 rfc2451
+des-deriv 64 ipsec-ciph-des-derived-01
+3des-deriv 192 no document
+rijndael-cbc 128/192/256 rfc3602
+aes-ctr 160/224/288 draft-ietf-ipsec-ciph-aes-ctr-03
+.Ed
+.Pp
+Note that the first 128 bits of a key for
+.Li aes-ctr
+will be used as AES key, and remaining 32 bits will be used as nonce.
+.Pp
+Followings are the list of compression algorithms that can be used as
+.Ar calgo
+in
+.Fl C Ar calgo
+of
+.Ar protocol
+parameter:
+.Pp
+.Bd -literal -offset indent
+algorithm comment
+deflate rfc2394
+.Ed
+.\"
+.Sh DIAGNOSTICS
+.Ex -std
+.\"
+.Sh EXAMPLES
+.Bd -literal -offset
+add 3ffe:501:4819::1 3ffe:501:481d::1 esp 123457
+ -E des-cbc 0x3ffe05014819ffff ;
+
+add -6 myhost.example.com yourhost.example.com ah 123456
+ -A hmac-sha1 "AH SA configuration!" ;
+
+add 10.0.11.41 10.0.11.33 esp 0x10001
+ -E des-cbc 0x3ffe05014819ffff
+ -A hmac-md5 "authentication!!" ;
+
+get 3ffe:501:4819::1 3ffe:501:481d::1 ah 123456 ;
+
+flush ;
+
+dump esp ;
+
+spdadd 10.0.11.41/32[21] 10.0.11.33/32[any] any
+ -P out ipsec esp/tunnel/192.168.0.1-192.168.1.2/require ;
+
+add 10.1.10.34 10.1.10.36 tcp 0x1000 -A tcp-md5 "TCP-MD5 BGP secret" ;
+
+.Ed
+.\"
+.Sh SEE ALSO
+.Xr ipsec_set_policy 3 ,
+.Xr racoon 8 ,
+.Xr sysctl 8
+.Rs
+.%T "Changed manual key configuration for IPsec"
+.%O "http://www.kame.net/newsletter/19991007/"
+.%D "October 1999"
+.Re
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+The utility was completely re-designed in June 1998.
+.\"
+.Sh BUGS
+The
+.Nm
+utility
+should report and handle syntax errors better.
+.Pp
+For IPsec gateway configuration,
+.Ar src_range
+and
+.Ar dst_range
+with TCP/UDP port number do not work, as the gateway does not reassemble
+packets
+(cannot inspect upper-layer headers).
diff --git a/usr.sbin/setkey/setkey.c b/usr.sbin/setkey/setkey.c
new file mode 100644
index 0000000..5bdd6df
--- /dev/null
+++ b/usr.sbin/setkey/setkey.c
@@ -0,0 +1,632 @@
+/* $FreeBSD$ */
+/* $KAME: setkey.c,v 1.28 2003/06/27 07:15:45 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <err.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+#include <netinet6/ipsec.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "libpfkey.h"
+
+void usage __P((void));
+int main __P((int, char **));
+int get_supported __P((void));
+void sendkeyshort __P((u_int));
+void promisc __P((void));
+int sendkeymsg __P((char *, size_t));
+int postproc __P((struct sadb_msg *, int));
+const char *numstr __P((int));
+void shortdump_hdr __P((void));
+void shortdump __P((struct sadb_msg *));
+static void printdate __P((void));
+static int32_t gmt2local __P((time_t));
+
+#define MODE_SCRIPT 1
+#define MODE_CMDDUMP 2
+#define MODE_CMDFLUSH 3
+#define MODE_PROMISC 4
+
+int so;
+
+int f_forever = 0;
+int f_all = 0;
+int f_verbose = 0;
+int f_mode = 0;
+int f_cmddump = 0;
+int f_policy = 0;
+int f_hexdump = 0;
+int f_tflag = 0;
+static time_t thiszone;
+
+extern int lineno;
+
+extern int parse __P((FILE **));
+
+void
+usage()
+{
+
+ printf("usage: setkey [-v] -c\n");
+ printf(" setkey [-v] -f filename\n");
+ printf(" setkey [-Palv] -D\n");
+ printf(" setkey [-Pv] -F\n");
+ printf(" setkey [-h] -x\n");
+ exit(1);
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+ FILE *fp = stdin;
+ int c;
+
+ if (ac == 1) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ thiszone = gmt2local(0);
+
+ while ((c = getopt(ac, av, "acdf:hlvxDFP")) != -1) {
+ switch (c) {
+ case 'c':
+ f_mode = MODE_SCRIPT;
+ fp = stdin;
+ break;
+ case 'f':
+ f_mode = MODE_SCRIPT;
+ if ((fp = fopen(optarg, "r")) == NULL) {
+ err(-1, "fopen");
+ /*NOTREACHED*/
+ }
+ break;
+ case 'D':
+ f_mode = MODE_CMDDUMP;
+ break;
+ case 'F':
+ f_mode = MODE_CMDFLUSH;
+ break;
+ case 'a':
+ f_all = 1;
+ break;
+ case 'l':
+ f_forever = 1;
+ break;
+ case 'h':
+ f_hexdump = 1;
+ break;
+ case 'x':
+ f_mode = MODE_PROMISC;
+ f_tflag++;
+ break;
+ case 'P':
+ f_policy = 1;
+ break;
+ case 'v':
+ f_verbose = 1;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ so = pfkey_open();
+ if (so < 0) {
+ perror("pfkey_open");
+ exit(1);
+ }
+
+ switch (f_mode) {
+ case MODE_CMDDUMP:
+ sendkeyshort(f_policy ? SADB_X_SPDDUMP: SADB_DUMP);
+ break;
+ case MODE_CMDFLUSH:
+ sendkeyshort(f_policy ? SADB_X_SPDFLUSH: SADB_FLUSH);
+ break;
+ case MODE_SCRIPT:
+ if (get_supported() < 0) {
+ errx(-1, "%s", ipsec_strerror());
+ /*NOTREACHED*/
+ }
+ if (parse(&fp))
+ exit (1);
+ break;
+ case MODE_PROMISC:
+ promisc();
+ /*NOTREACHED*/
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+
+ exit(0);
+}
+
+int
+get_supported()
+{
+
+ if (pfkey_send_register(so, SADB_SATYPE_UNSPEC) < 0)
+ return -1;
+
+ if (pfkey_recv_register(so) < 0)
+ return -1;
+
+ return 0;
+}
+
+void
+sendkeyshort(type)
+ u_int type;
+{
+ struct sadb_msg msg;
+
+ msg.sadb_msg_version = PF_KEY_V2;
+ msg.sadb_msg_type = type;
+ msg.sadb_msg_errno = 0;
+ msg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
+ msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
+ msg.sadb_msg_reserved = 0;
+ msg.sadb_msg_seq = 0;
+ msg.sadb_msg_pid = getpid();
+
+ sendkeymsg((char *)&msg, sizeof(msg));
+
+ return;
+}
+
+void
+promisc()
+{
+ struct sadb_msg msg;
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ ssize_t l;
+
+ msg.sadb_msg_version = PF_KEY_V2;
+ msg.sadb_msg_type = SADB_X_PROMISC;
+ msg.sadb_msg_errno = 0;
+ msg.sadb_msg_satype = 1;
+ msg.sadb_msg_len = PFKEY_UNIT64(sizeof(msg));
+ msg.sadb_msg_reserved = 0;
+ msg.sadb_msg_seq = 0;
+ msg.sadb_msg_pid = getpid();
+
+ if ((l = send(so, &msg, sizeof(msg), 0)) < 0) {
+ err(1, "send");
+ /*NOTREACHED*/
+ }
+
+ while (1) {
+ struct sadb_msg *base;
+
+ if ((l = recv(so, rbuf, sizeof(*base), MSG_PEEK)) < 0) {
+ err(1, "recv");
+ /*NOTREACHED*/
+ }
+
+ if (l != sizeof(*base))
+ continue;
+
+ base = (struct sadb_msg *)rbuf;
+ if ((l = recv(so, rbuf, PFKEY_UNUNIT64(base->sadb_msg_len),
+ 0)) < 0) {
+ err(1, "recv");
+ /*NOTREACHED*/
+ }
+ printdate();
+ if (f_hexdump) {
+ int i;
+ for (i = 0; i < l; i++) {
+ if (i % 16 == 0)
+ printf("%08x: ", i);
+ printf("%02x ", rbuf[i] & 0xff);
+ if (i % 16 == 15)
+ printf("\n");
+ }
+ if (l % 16)
+ printf("\n");
+ }
+ /* adjust base pointer for promisc mode */
+ if (base->sadb_msg_type == SADB_X_PROMISC) {
+ if ((ssize_t)sizeof(*base) < l)
+ base++;
+ else
+ base = NULL;
+ }
+ if (base) {
+ kdebug_sadb(base);
+ printf("\n");
+ fflush(stdout);
+ }
+ }
+}
+
+int
+sendkeymsg(buf, len)
+ char *buf;
+ size_t len;
+{
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ ssize_t l;
+ struct sadb_msg *msg;
+
+ {
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ perror("setsockopt");
+ goto end;
+ }
+ }
+
+ if (f_forever)
+ shortdump_hdr();
+again:
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)buf);
+ printf("\n");
+ }
+ if (f_hexdump) {
+ int i;
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printf("%08x: ", i);
+ printf("%02x ", buf[i] & 0xff);
+ if (i % 16 == 15)
+ printf("\n");
+ }
+ if (len % 16)
+ printf("\n");
+ }
+
+ if ((l = send(so, buf, len, 0)) < 0) {
+ perror("send");
+ goto end;
+ }
+
+ msg = (struct sadb_msg *)rbuf;
+ do {
+ if ((l = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
+ perror("recv");
+ goto end;
+ }
+
+ if (PFKEY_UNUNIT64(msg->sadb_msg_len) != l) {
+ warnx("invalid keymsg length");
+ break;
+ }
+
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)rbuf);
+ printf("\n");
+ }
+ if (postproc(msg, l) < 0)
+ break;
+ } while (msg->sadb_msg_errno || msg->sadb_msg_seq);
+
+ if (f_forever) {
+ fflush(stdout);
+ sleep(1);
+ goto again;
+ }
+
+end:
+ return(0);
+}
+
+int
+postproc(msg, len)
+ struct sadb_msg *msg;
+ int len;
+{
+
+ if (msg->sadb_msg_errno != 0) {
+ char inf[80];
+ const char *errmsg = NULL;
+
+ if (f_mode == MODE_SCRIPT)
+ snprintf(inf, sizeof(inf), "The result of line %d: ", lineno);
+ else
+ inf[0] = '\0';
+
+ switch (msg->sadb_msg_errno) {
+ case ENOENT:
+ switch (msg->sadb_msg_type) {
+ case SADB_DELETE:
+ case SADB_GET:
+ case SADB_X_SPDDELETE:
+ errmsg = "No entry";
+ break;
+ case SADB_DUMP:
+ errmsg = "No SAD entries";
+ break;
+ case SADB_X_SPDDUMP:
+ errmsg = "No SPD entries";
+ break;
+ }
+ break;
+ default:
+ errmsg = strerror(msg->sadb_msg_errno);
+ }
+ printf("%s%s.\n", inf, errmsg);
+ return(-1);
+ }
+
+ switch (msg->sadb_msg_type) {
+ case SADB_GET:
+ pfkey_sadump(msg);
+ break;
+
+ case SADB_DUMP:
+ /* filter out DEAD SAs */
+ if (!f_all) {
+ caddr_t mhp[SADB_EXT_MAX + 1];
+ struct sadb_sa *sa;
+ pfkey_align(msg, mhp);
+ pfkey_check(mhp);
+ if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
+ if (sa->sadb_sa_state == SADB_SASTATE_DEAD)
+ break;
+ }
+ }
+ if (f_forever)
+ shortdump(msg);
+ else
+ pfkey_sadump(msg);
+ msg = (struct sadb_msg *)((caddr_t)msg +
+ PFKEY_UNUNIT64(msg->sadb_msg_len));
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)msg);
+ printf("\n");
+ }
+ break;
+
+ case SADB_X_SPDDUMP:
+ pfkey_spdump(msg);
+ if (msg->sadb_msg_seq == 0) break;
+ msg = (struct sadb_msg *)((caddr_t)msg +
+ PFKEY_UNUNIT64(msg->sadb_msg_len));
+ if (f_verbose) {
+ kdebug_sadb((struct sadb_msg *)msg);
+ printf("\n");
+ }
+ break;
+ }
+
+ return(0);
+}
+
+/*------------------------------------------------------------*/
+static const char *satype[] = {
+ NULL, NULL, "ah", "esp"
+};
+static const char *sastate[] = {
+ "L", "M", "D", "d"
+};
+static const char *ipproto[] = {
+/*0*/ "ip", "icmp", "igmp", "ggp", "ip4",
+ NULL, "tcp", NULL, "egp", NULL,
+/*10*/ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, "udp", NULL, NULL,
+/*20*/ NULL, NULL, "idp", NULL, NULL,
+ NULL, NULL, NULL, NULL, "tp",
+/*30*/ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+/*40*/ NULL, "ip6", NULL, "rt6", "frag6",
+ NULL, "rsvp", "gre", NULL, NULL,
+/*50*/ "esp", "ah", NULL, NULL, NULL,
+ NULL, NULL, NULL, "icmp6", "none",
+/*60*/ "dst6",
+};
+
+#define STR_OR_ID(x, tab) \
+ (((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x))
+
+const char *
+numstr(x)
+ int x;
+{
+ static char buf[20];
+ snprintf(buf, sizeof(buf), "#%d", x);
+ return buf;
+}
+
+void
+shortdump_hdr()
+{
+ printf("%-4s %-3s %-1s %-8s %-7s %s -> %s\n",
+ "time", "p", "s", "spi", "ltime", "src", "dst");
+}
+
+void
+shortdump(msg)
+ struct sadb_msg *msg;
+{
+ caddr_t mhp[SADB_EXT_MAX + 1];
+ char buf[NI_MAXHOST], pbuf[NI_MAXSERV];
+ struct sadb_sa *sa;
+ struct sadb_address *saddr;
+ struct sadb_lifetime *lts, *lth, *ltc;
+ struct sockaddr *s;
+ u_int t;
+ time_t cur = time(0);
+
+ pfkey_align(msg, mhp);
+ pfkey_check(mhp);
+
+ printf("%02lu%02lu", (u_long)(cur % 3600) / 60, (u_long)(cur % 60));
+
+ printf(" %-3s", STR_OR_ID(msg->sadb_msg_satype, satype));
+
+ if ((sa = (struct sadb_sa *)mhp[SADB_EXT_SA]) != NULL) {
+ printf(" %-1s", STR_OR_ID(sa->sadb_sa_state, sastate));
+ printf(" %08x", (u_int32_t)ntohl(sa->sadb_sa_spi));
+ } else
+ printf("%-1s %-8s", "?", "?");
+
+ lts = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT];
+ lth = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD];
+ ltc = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_CURRENT];
+ if (lts && lth && ltc) {
+ if (ltc->sadb_lifetime_addtime == 0)
+ t = (u_long)0;
+ else
+ t = (u_long)(cur - ltc->sadb_lifetime_addtime);
+ if (t >= 1000)
+ strlcpy(buf, " big/", sizeof(buf));
+ else
+ snprintf(buf, sizeof(buf), " %3lu/", (u_long)t);
+ printf("%s", buf);
+
+ t = (u_long)lth->sadb_lifetime_addtime;
+ if (t >= 1000)
+ strlcpy(buf, "big", sizeof(buf));
+ else
+ snprintf(buf, sizeof(buf), "%-3lu", (u_long)t);
+ printf("%s", buf);
+ } else
+ printf(" ??\?/???"); /* backslash to avoid trigraph ??/ */
+
+ printf(" ");
+
+ if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]) != NULL) {
+ if (saddr->sadb_address_proto)
+ printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
+ s = (struct sockaddr *)(saddr + 1);
+ getnameinfo(s, s->sa_len, buf, sizeof(buf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ if (strcmp(pbuf, "0") != 0)
+ printf("%s[%s]", buf, pbuf);
+ else
+ printf("%s", buf);
+ } else
+ printf("?");
+
+ printf(" -> ");
+
+ if ((saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]) != NULL) {
+ if (saddr->sadb_address_proto)
+ printf("%s ", STR_OR_ID(saddr->sadb_address_proto, ipproto));
+
+ s = (struct sockaddr *)(saddr + 1);
+ getnameinfo(s, s->sa_len, buf, sizeof(buf),
+ pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV);
+ if (strcmp(pbuf, "0") != 0)
+ printf("%s[%s]", buf, pbuf);
+ else
+ printf("%s", buf);
+ } else
+ printf("?");
+
+ printf("\n");
+}
+
+/* From: tcpdump(1):gmt2local.c and util.c */
+/*
+ * Print the timestamp
+ */
+static void
+printdate()
+{
+ struct timeval tp;
+ int s;
+
+ if (gettimeofday(&tp, NULL) == -1) {
+ perror("gettimeofday");
+ return;
+ }
+
+ if (f_tflag == 1) {
+ /* Default */
+ s = (tp.tv_sec + thiszone ) % 86400;
+ (void)printf("%02d:%02d:%02d.%06u ",
+ s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tp.tv_usec);
+ } else if (f_tflag > 1) {
+ /* Unix timeval style */
+ (void)printf("%u.%06u ",
+ (u_int32_t)tp.tv_sec, (u_int32_t)tp.tv_usec);
+ }
+
+ printf("\n");
+}
+
+/*
+ * Returns the difference between gmt and local time in seconds.
+ * Use gmtime() and localtime() to keep things simple.
+ */
+int32_t
+gmt2local(time_t t)
+{
+ register int dt, dir;
+ register struct tm *gmt, *loc;
+ struct tm sgmt;
+
+ if (t == 0)
+ t = time(NULL);
+ gmt = &sgmt;
+ *gmt = *gmtime(&t);
+ loc = localtime(&t);
+ dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
+ (loc->tm_min - gmt->tm_min) * 60;
+
+ /*
+ * If the year or julian day is different, we span 00:00 GMT
+ * and must add or subtract a day. Check the year first to
+ * avoid problems when the julian day wraps.
+ */
+ dir = loc->tm_year - gmt->tm_year;
+ if (dir == 0)
+ dir = loc->tm_yday - gmt->tm_yday;
+ dt += dir * 24 * 60 * 60;
+
+ return (dt);
+}
diff --git a/usr.sbin/setkey/test-pfkey.c b/usr.sbin/setkey/test-pfkey.c
new file mode 100644
index 0000000..b1fb238
--- /dev/null
+++ b/usr.sbin/setkey/test-pfkey.c
@@ -0,0 +1,531 @@
+/* $FreeBSD$ */
+/* $KAME: test-pfkey.c,v 1.4 2000/06/07 00:29:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/pfkeyv2.h>
+#include <netinet/in.h>
+#include <netkey/keydb.h>
+#include <netkey/key_var.h>
+#include <netkey/key_debug.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+u_char m_buf[BUFSIZ];
+u_int m_len;
+char *pname;
+
+void Usage __P((void));
+int sendkeymsg __P((void));
+void key_setsadbmsg __P((u_int));
+void key_setsadbsens __P((void));
+void key_setsadbprop __P((void));
+void key_setsadbid __P((u_int, caddr_t));
+void key_setsadblft __P((u_int, u_int));
+void key_setspirange __P((void));
+void key_setsadbkey __P((u_int, caddr_t));
+void key_setsadbsa __P((void));
+void key_setsadbaddr __P((u_int, u_int, caddr_t));
+void key_setsadbextbuf __P((caddr_t, int, caddr_t, int, caddr_t, int));
+
+void
+Usage()
+{
+ printf("Usage:\t%s number\n", pname);
+ exit(0);
+}
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+ pname = *av;
+
+ if (ac == 1) Usage();
+
+ key_setsadbmsg(atoi(*(av+1)));
+ sendkeymsg();
+
+ exit(0);
+}
+
+/* %%% */
+int
+sendkeymsg()
+{
+ u_char rbuf[1024 * 32]; /* XXX: Enough ? Should I do MSG_PEEK ? */
+ int so, len;
+
+ if ((so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) {
+ perror("socket(PF_KEY)");
+ goto end;
+ }
+#if 0
+ {
+#include <sys/time.h>
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ if (setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ perror("setsockopt");
+ goto end;
+ }
+ }
+#endif
+
+ pfkey_sadump((struct sadb_msg *)m_buf);
+
+ if ((len = send(so, m_buf, m_len, 0)) < 0) {
+ perror("send");
+ goto end;
+ }
+
+ if ((len = recv(so, rbuf, sizeof(rbuf), 0)) < 0) {
+ perror("recv");
+ goto end;
+ }
+
+ pfkey_sadump((struct sadb_msg *)rbuf);
+
+end:
+ (void)close(so);
+ return(0);
+}
+
+void
+key_setsadbmsg(type)
+ u_int type;
+{
+ struct sadb_msg m_msg;
+
+ memset(&m_msg, 0, sizeof(m_msg));
+ m_msg.sadb_msg_version = PF_KEY_V2;
+ m_msg.sadb_msg_type = type;
+ m_msg.sadb_msg_errno = 0;
+ m_msg.sadb_msg_satype = SADB_SATYPE_ESP;
+#if 0
+ m_msg.sadb_msg_reserved = 0;
+#endif
+ m_msg.sadb_msg_seq = 0;
+ m_msg.sadb_msg_pid = getpid();
+
+ m_len = sizeof(struct sadb_msg);
+ memcpy(m_buf, &m_msg, m_len);
+
+ switch (type) {
+ case SADB_GETSPI:
+ /*<base, address(SD), SPI range>*/
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "127.0.0.1");
+ key_setspirange();
+ /*<base, SA(*), address(SD)>*/
+ break;
+
+ case SADB_ADD:
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ key(AE), (identity(SD),) (sensitivity)> */
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ case SADB_UPDATE:
+ key_setsadbsa();
+ key_setsadblft(SADB_EXT_LIFETIME_HARD, 10);
+ key_setsadblft(SADB_EXT_LIFETIME_SOFT, 5);
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ /* XXX key_setsadbkey(SADB_EXT_KEY_AUTH, "abcde"); */
+ key_setsadbkey(SADB_EXT_KEY_AUTH, "1234567812345678");
+ key_setsadbkey(SADB_EXT_KEY_ENCRYPT, "12345678");
+ key_setsadbid(SADB_EXT_IDENTITY_SRC, "hoge1234@hoge.com");
+ key_setsadbid(SADB_EXT_IDENTITY_DST, "hage5678@hage.net");
+ key_setsadbsens();
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ (identity(SD),) (sensitivity)> */
+ break;
+
+ case SADB_DELETE:
+ /* <base, SA(*), address(SDP)> */
+ key_setsadbsa();
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ /* <base, SA(*), address(SDP)> */
+ break;
+
+ case SADB_GET:
+ /* <base, SA(*), address(SDP)> */
+ key_setsadbsa();
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ /* <base, SA, (lifetime(HSC),) address(SD), (address(P),)
+ key(AE), (identity(SD),) (sensitivity)> */
+ break;
+
+ case SADB_ACQUIRE:
+ /* <base, address(SD), (address(P),) (identity(SD),)
+ (sensitivity,) proposal> */
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ key_setsadbaddr(SADB_EXT_ADDRESS_PROXY, AF_INET6, "3ffe::1");
+ key_setsadbid(SADB_EXT_IDENTITY_SRC, "hoge1234@hoge.com");
+ key_setsadbid(SADB_EXT_IDENTITY_DST, "hage5678@hage.net");
+ key_setsadbsens();
+ key_setsadbprop();
+ /* <base, address(SD), (address(P),) (identity(SD),)
+ (sensitivity,) proposal> */
+ break;
+
+ case SADB_REGISTER:
+ /* <base> */
+ /* <base, supported> */
+ break;
+
+ case SADB_EXPIRE:
+ case SADB_FLUSH:
+ break;
+
+ case SADB_DUMP:
+ break;
+
+ case SADB_X_PROMISC:
+ /* <base> */
+ /* <base, base(, others)> */
+ break;
+
+ case SADB_X_PCHANGE:
+ break;
+
+ /* for SPD management */
+ case SADB_X_SPDFLUSH:
+ case SADB_X_SPDDUMP:
+ break;
+
+ case SADB_X_SPDADD:
+#if 0
+ {
+ struct sadb_x_policy m_policy;
+
+ m_policy.sadb_x_policy_len = PFKEY_UNIT64(sizeof(m_policy));
+ m_policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+ m_policy.sadb_x_policy_type = SADB_X_PL_IPSEC;
+ m_policy.sadb_x_policy_esp_trans = 1;
+ m_policy.sadb_x_policy_ah_trans = 2;
+ m_policy.sadb_x_policy_esp_network = 3;
+ m_policy.sadb_x_policy_ah_network = 4;
+ m_policy.sadb_x_policy_reserved = 0;
+
+ memcpy(m_buf + m_len, &m_policy, sizeof(struct sadb_x_policy));
+ m_len += sizeof(struct sadb_x_policy);
+ }
+#endif
+
+ case SADB_X_SPDDELETE:
+ key_setsadbaddr(SADB_EXT_ADDRESS_SRC, AF_INET, "192.168.1.1");
+ key_setsadbaddr(SADB_EXT_ADDRESS_DST, AF_INET, "10.0.3.4");
+ break;
+ }
+
+ ((struct sadb_msg *)m_buf)->sadb_msg_len = PFKEY_UNIT64(m_len);
+
+ return;
+}
+
+void
+key_setsadbsens()
+{
+ struct sadb_sens m_sens;
+ u_char buf[64];
+ u_int s, i, slen, ilen, len;
+
+ /* make sens & integ */
+ s = htonl(0x01234567);
+ i = htonl(0x89abcdef);
+ slen = sizeof(s);
+ ilen = sizeof(i);
+ memcpy(buf, &s, slen);
+ memcpy(buf + slen, &i, ilen);
+
+ len = sizeof(m_sens) + PFKEY_ALIGN8(slen) + PFKEY_ALIGN8(ilen);
+ m_sens.sadb_sens_len = PFKEY_UNIT64(len);
+ m_sens.sadb_sens_exttype = SADB_EXT_SENSITIVITY;
+ m_sens.sadb_sens_dpd = 1;
+ m_sens.sadb_sens_sens_level = 2;
+ m_sens.sadb_sens_sens_len = PFKEY_ALIGN8(slen);
+ m_sens.sadb_sens_integ_level = 3;
+ m_sens.sadb_sens_integ_len = PFKEY_ALIGN8(ilen);
+ m_sens.sadb_sens_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_sens, sizeof(struct sadb_sens),
+ buf, slen + ilen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbprop()
+{
+ struct sadb_prop m_prop;
+ struct sadb_comb *m_comb;
+ u_char buf[256];
+ u_int len = sizeof(m_prop) + sizeof(m_comb) * 2;
+
+ /* make prop & comb */
+ m_prop.sadb_prop_len = PFKEY_UNIT64(len);
+ m_prop.sadb_prop_exttype = SADB_EXT_PROPOSAL;
+ m_prop.sadb_prop_replay = 0;
+ m_prop.sadb_prop_reserved[0] = 0;
+ m_prop.sadb_prop_reserved[1] = 0;
+ m_prop.sadb_prop_reserved[2] = 0;
+
+ /* the 1st is ESP DES-CBC HMAC-MD5 */
+ m_comb = (struct sadb_comb *)buf;
+ m_comb->sadb_comb_auth = SADB_AALG_MD5HMAC;
+ m_comb->sadb_comb_encrypt = SADB_EALG_DESCBC;
+ m_comb->sadb_comb_flags = 0;
+ m_comb->sadb_comb_auth_minbits = 8;
+ m_comb->sadb_comb_auth_maxbits = 96;
+ m_comb->sadb_comb_encrypt_minbits = 64;
+ m_comb->sadb_comb_encrypt_maxbits = 64;
+ m_comb->sadb_comb_reserved = 0;
+ m_comb->sadb_comb_soft_allocations = 0;
+ m_comb->sadb_comb_hard_allocations = 0;
+ m_comb->sadb_comb_soft_bytes = 0;
+ m_comb->sadb_comb_hard_bytes = 0;
+ m_comb->sadb_comb_soft_addtime = 0;
+ m_comb->sadb_comb_hard_addtime = 0;
+ m_comb->sadb_comb_soft_usetime = 0;
+ m_comb->sadb_comb_hard_usetime = 0;
+
+ /* the 2st is ESP 3DES-CBC and AH HMAC-SHA1 */
+ m_comb = (struct sadb_comb *)(buf + sizeof(*m_comb));
+ m_comb->sadb_comb_auth = SADB_AALG_SHA1HMAC;
+ m_comb->sadb_comb_encrypt = SADB_EALG_3DESCBC;
+ m_comb->sadb_comb_flags = 0;
+ m_comb->sadb_comb_auth_minbits = 8;
+ m_comb->sadb_comb_auth_maxbits = 96;
+ m_comb->sadb_comb_encrypt_minbits = 64;
+ m_comb->sadb_comb_encrypt_maxbits = 64;
+ m_comb->sadb_comb_reserved = 0;
+ m_comb->sadb_comb_soft_allocations = 0;
+ m_comb->sadb_comb_hard_allocations = 0;
+ m_comb->sadb_comb_soft_bytes = 0;
+ m_comb->sadb_comb_hard_bytes = 0;
+ m_comb->sadb_comb_soft_addtime = 0;
+ m_comb->sadb_comb_hard_addtime = 0;
+ m_comb->sadb_comb_soft_usetime = 0;
+ m_comb->sadb_comb_hard_usetime = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_prop, sizeof(struct sadb_prop),
+ buf, sizeof(*m_comb) * 2);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbid(ext, str)
+ u_int ext;
+ caddr_t str;
+{
+ struct sadb_ident m_id;
+ u_int idlen = strlen(str), len;
+
+ len = sizeof(m_id) + PFKEY_ALIGN8(idlen);
+ m_id.sadb_ident_len = PFKEY_UNIT64(len);
+ m_id.sadb_ident_exttype = ext;
+ m_id.sadb_ident_type = SADB_IDENTTYPE_USERFQDN;
+ m_id.sadb_ident_reserved = 0;
+ m_id.sadb_ident_id = getpid();
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_id, sizeof(struct sadb_ident),
+ str, idlen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadblft(ext, time)
+ u_int ext, time;
+{
+ struct sadb_lifetime m_lft;
+
+ m_lft.sadb_lifetime_len = PFKEY_UNIT64(sizeof(m_lft));
+ m_lft.sadb_lifetime_exttype = ext;
+ m_lft.sadb_lifetime_allocations = 0x2;
+ m_lft.sadb_lifetime_bytes = 0x1000;
+ m_lft.sadb_lifetime_addtime = time;
+ m_lft.sadb_lifetime_usetime = 0x0020;
+
+ memcpy(m_buf + m_len, &m_lft, sizeof(struct sadb_lifetime));
+ m_len += sizeof(struct sadb_lifetime);
+
+ return;
+}
+
+void
+key_setspirange()
+{
+ struct sadb_spirange m_spi;
+
+ m_spi.sadb_spirange_len = PFKEY_UNIT64(sizeof(m_spi));
+ m_spi.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+ m_spi.sadb_spirange_min = 0x00001000;
+ m_spi.sadb_spirange_max = 0x00002000;
+ m_spi.sadb_spirange_reserved = 0;
+
+ memcpy(m_buf + m_len, &m_spi, sizeof(struct sadb_spirange));
+ m_len += sizeof(struct sadb_spirange);
+
+ return;
+}
+
+void
+key_setsadbkey(ext, str)
+ u_int ext;
+ caddr_t str;
+{
+ struct sadb_key m_key;
+ u_int keylen = strlen(str);
+ u_int len;
+
+ len = sizeof(struct sadb_key) + PFKEY_ALIGN8(keylen);
+ m_key.sadb_key_len = PFKEY_UNIT64(len);
+ m_key.sadb_key_exttype = ext;
+ m_key.sadb_key_bits = keylen * 8;
+ m_key.sadb_key_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_key, sizeof(struct sadb_key),
+ str, keylen);
+ m_len += len;
+
+ return;
+}
+
+void
+key_setsadbsa()
+{
+ struct sadb_sa m_sa;
+
+ m_sa.sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa));
+ m_sa.sadb_sa_exttype = SADB_EXT_SA;
+ m_sa.sadb_sa_spi = htonl(0x12345678);
+ m_sa.sadb_sa_replay = 4;
+ m_sa.sadb_sa_state = 0;
+ m_sa.sadb_sa_auth = SADB_AALG_MD5HMAC;
+ m_sa.sadb_sa_encrypt = SADB_EALG_DESCBC;
+ m_sa.sadb_sa_flags = 0;
+
+ memcpy(m_buf + m_len, &m_sa, sizeof(struct sadb_sa));
+ m_len += sizeof(struct sadb_sa);
+
+ return;
+}
+
+void
+key_setsadbaddr(ext, af, str)
+ u_int ext, af;
+ caddr_t str;
+{
+ struct sadb_address m_addr;
+ u_int len;
+ struct addrinfo hints, *res;
+ const char *serv;
+ int plen;
+
+ switch (af) {
+ case AF_INET:
+ plen = sizeof(struct in_addr) << 3;
+ break;
+ case AF_INET6:
+ plen = sizeof(struct in6_addr) << 3;
+ break;
+ default:
+ /* XXX bark */
+ exit(1);
+ }
+
+ /* make sockaddr buffer */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ serv = (ext == SADB_EXT_ADDRESS_PROXY ? "0" : "4660"); /*0x1234*/
+ if (getaddrinfo(str, serv, &hints, &res) != 0 || res->ai_next) {
+ /* XXX bark */
+ exit(1);
+ }
+
+ len = sizeof(struct sadb_address) + PFKEY_ALIGN8(res->ai_addrlen);
+ m_addr.sadb_address_len = PFKEY_UNIT64(len);
+ m_addr.sadb_address_exttype = ext;
+ m_addr.sadb_address_proto =
+ (ext == SADB_EXT_ADDRESS_PROXY ? 0 : IPPROTO_TCP);
+ m_addr.sadb_address_prefixlen = plen;
+ m_addr.sadb_address_reserved = 0;
+
+ key_setsadbextbuf(m_buf, m_len,
+ (caddr_t)&m_addr, sizeof(struct sadb_address),
+ (caddr_t)res->ai_addr, res->ai_addrlen);
+ m_len += len;
+
+ freeaddrinfo(res);
+
+ return;
+}
+
+void
+key_setsadbextbuf(dst, off, ebuf, elen, vbuf, vlen)
+ caddr_t dst, ebuf, vbuf;
+ int off, elen, vlen;
+{
+ memset(dst + off, 0, elen + vlen);
+ memcpy(dst + off, (caddr_t)ebuf, elen);
+ memcpy(dst + off + elen, vbuf, vlen);
+
+ return;
+}
+
diff --git a/usr.sbin/setkey/test-policy.c b/usr.sbin/setkey/test-policy.c
new file mode 100644
index 0000000..27cd478
--- /dev/null
+++ b/usr.sbin/setkey/test-policy.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet6/in6.h>
+#include <netkey/keyv2.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <netinet6/ipsec.h>
+
+char *requests[] = {
+"must_error", /* must be error */
+"ipsec must_error", /* must be error */
+"ipsec esp/must_error", /* must be error */
+"discard",
+"none",
+"entrust",
+"bypass", /* may be error */
+"ipsec esp", /* must be error */
+"ipsec ah/require",
+"ipsec ah/use/",
+"ipsec esp/require ah/default/203.178.141.194",
+"ipsec ah/use/203.178.141.195 esp/use/203.178.141.194",
+"ipsec esp/elf.wide.ydc.co.jp esp/www.wide.ydc.co.jp"
+"
+ipsec esp/require ah/use esp/require/10.0.0.1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1
+ah/use/3ffe:501:481d::1 ah/use/3ffe:501:481d::1ah/use/3ffe:501:481d::1
+",
+};
+
+u_char *p_secpolicy;
+
+int test(char *buf, int family);
+char *setpolicy(char *req);
+
+main()
+{
+ int i;
+ char *buf;
+
+ for (i = 0; i < sizeof(requests)/sizeof(requests[0]); i++) {
+ printf("* requests:[%s]\n", requests[i]);
+ if ((buf = setpolicy(requests[i])) == NULL)
+ continue;
+ printf("\tsetlen:%d\n", PFKEY_EXTLEN(buf));
+
+ printf("\tPF_INET:\n");
+ test(buf, PF_INET);
+
+ printf("\tPF_INET6:\n");
+ test(buf, PF_INET6);
+ free(buf);
+ }
+}
+
+int test(char *policy, int family)
+{
+ int so, proto, optname;
+ int len;
+ char getbuf[1024];
+
+ switch (family) {
+ case PF_INET:
+ proto = IPPROTO_IP;
+ optname = IP_IPSEC_POLICY;
+ break;
+ case PF_INET6:
+ proto = IPPROTO_IPV6;
+ optname = IPV6_IPSEC_POLICY;
+ break;
+ }
+
+ if ((so = socket(family, SOCK_DGRAM, 0)) < 0)
+ perror("socket");
+
+ if (setsockopt(so, proto, optname, policy, PFKEY_EXTLEN(policy)) < 0)
+ perror("setsockopt");
+
+ len = sizeof(getbuf);
+ memset(getbuf, 0, sizeof(getbuf));
+ if (getsockopt(so, proto, optname, getbuf, &len) < 0)
+ perror("getsockopt");
+
+ {
+ char *buf = NULL;
+
+ printf("\tgetlen:%d\n", len);
+
+ if ((buf = ipsec_dump_policy(getbuf, NULL)) == NULL)
+ ipsec_strerror();
+ else
+ printf("\t[%s]\n", buf);
+
+ free(buf);
+ }
+
+ close (so);
+}
+
+char *setpolicy(char *req)
+{
+ int len;
+ char *buf;
+
+ if ((len = ipsec_get_policylen(req)) < 0) {
+ printf("ipsec_get_policylen: %s\n", ipsec_strerror());
+ return NULL;
+ }
+
+ if ((buf = malloc(len)) == NULL) {
+ perror("malloc");
+ return NULL;
+ }
+
+ if ((len = ipsec_set_policy(buf, len, req)) < 0) {
+ printf("ipsec_set_policy: %s\n", ipsec_strerror());
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
diff --git a/usr.sbin/setkey/token.l b/usr.sbin/setkey/token.l
new file mode 100644
index 0000000..74c1e17
--- /dev/null
+++ b/usr.sbin/setkey/token.l
@@ -0,0 +1,286 @@
+/* $FreeBSD$ */
+/* $KAME: token.l,v 1.43 2003/07/25 09:35:28 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+#include <netinet/in.h>
+#include <netinet6/ipsec.h>
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "vchar.h"
+#include "y.tab.h"
+
+int lineno = 1;
+
+extern u_char m_buf[BUFSIZ];
+extern u_int m_len;
+extern int f_debug;
+
+int yylex __P((void));
+void yyfatal __P((const char *s));
+void yyerror __P((const char *s));
+extern void parse_init __P((void));
+int parse __P((FILE **));
+int yyparse __P((void));
+%}
+
+/* common section */
+nl \n
+ws [ \t]+
+digit [0-9]
+letter [0-9A-Za-z]
+hexdigit [0-9A-Fa-f]
+dot \.
+hyphen \-
+slash \/
+blcl \[
+elcl \]
+semi \;
+comment \#.*
+quotedstring \"[^"]*\"
+decstring {digit}+
+hexstring 0[xX]{hexdigit}+
+ipaddress [a-fA-F0-9:]([a-fA-F0-9:\.]*|[a-fA-F0-9:\.]*%[a-zA-Z0-9]*)
+ipaddrmask {slash}{digit}{1,3}
+name {letter}(({letter}|{digit}|{hyphen})*({letter}|{digit}))*
+hostname {name}(({dot}{name})+{dot}?)?
+
+%s S_PL S_AUTHALG S_ENCALG
+
+%%
+
+add { return(ADD); }
+delete { return(DELETE); }
+deleteall { return(DELETEALL); }
+get { return(GET); }
+flush { return(FLUSH); }
+dump { return(DUMP); }
+
+ /* for management SPD */
+spdadd { return(SPDADD); }
+spddelete { return(SPDDELETE); }
+spddump { return(SPDDUMP); }
+spdflush { return(SPDFLUSH); }
+tagged { return(TAGGED); }
+{hyphen}P { BEGIN S_PL; return(F_POLICY); }
+<S_PL>[a-zA-Z0-9:\.\-_/ \n\t][a-zA-Z0-9:\.%\-_/ \n\t]* {
+ yymore();
+
+ /* count up for nl */
+ {
+ char *p;
+ for (p = yytext; *p != '\0'; p++)
+ if (*p == '\n')
+ lineno++;
+ }
+
+ yylval.val.len = strlen(yytext);
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+
+ return(PL_REQUESTS);
+ }
+<S_PL>{semi} { BEGIN INITIAL; return(EOT); }
+
+ /* address resolution flags */
+{hyphen}[n46][n46]* {
+ yylval.val.len = strlen(yytext);
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ return(F_AIFLAGS);
+ }
+
+ /* security protocols */
+ah { yylval.num = 0; return(PR_AH); }
+esp { yylval.num = 0; return(PR_ESP); }
+ah-old { yylval.num = 1; return(PR_AH); }
+esp-old { yylval.num = 1; return(PR_ESP); }
+ipcomp { yylval.num = 0; return(PR_IPCOMP); }
+tcp { yylval.num = 0; return(PR_TCP); }
+
+ /* authentication alogorithm */
+{hyphen}A { BEGIN S_AUTHALG; return(F_AUTH); }
+<S_AUTHALG>hmac-md5 { yylval.num = SADB_AALG_MD5HMAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha1 { yylval.num = SADB_AALG_SHA1HMAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>keyed-md5 { yylval.num = SADB_X_AALG_MD5; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>keyed-sha1 { yylval.num = SADB_X_AALG_SHA; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha2-256 { yylval.num = SADB_X_AALG_SHA2_256; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha2-384 { yylval.num = SADB_X_AALG_SHA2_384; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-sha2-512 { yylval.num = SADB_X_AALG_SHA2_512; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>hmac-ripemd160 { yylval.num = SADB_X_AALG_RIPEMD160HMAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>aes-xcbc-mac { yylval.num = SADB_X_AALG_AES_XCBC_MAC; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>tcp-md5 { yylval.num = SADB_X_AALG_TCP_MD5; BEGIN INITIAL; return(ALG_AUTH); }
+<S_AUTHALG>null { yylval.num = SADB_X_AALG_NULL; BEGIN INITIAL; return(ALG_AUTH_NOKEY); }
+
+ /* encryption alogorithm */
+{hyphen}E { BEGIN S_ENCALG; return(F_ENC); }
+<S_ENCALG>des-cbc { yylval.num = SADB_EALG_DESCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>3des-cbc { yylval.num = SADB_EALG_3DESCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>null { yylval.num = SADB_EALG_NULL; BEGIN INITIAL; return(ALG_ENC_NOKEY); }
+<S_ENCALG>simple { yylval.num = SADB_EALG_NULL; BEGIN INITIAL; return(ALG_ENC_OLD); }
+<S_ENCALG>blowfish-cbc { yylval.num = SADB_X_EALG_BLOWFISHCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>cast128-cbc { yylval.num = SADB_X_EALG_CAST128CBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>des-deriv { yylval.num = SADB_EALG_DESCBC; BEGIN INITIAL; return(ALG_ENC_DESDERIV); }
+<S_ENCALG>des-32iv { yylval.num = SADB_EALG_DESCBC; BEGIN INITIAL; return(ALG_ENC_DES32IV); }
+<S_ENCALG>rijndael-cbc { yylval.num = SADB_X_EALG_RIJNDAELCBC; BEGIN INITIAL; return(ALG_ENC); }
+<S_ENCALG>aes-ctr { yylval.num = SADB_X_EALG_AESCTR; BEGIN INITIAL; return(ALG_ENC); }
+
+ /* compression algorithms */
+{hyphen}C { return(F_COMP); }
+oui { yylval.num = SADB_X_CALG_OUI; return(ALG_COMP); }
+deflate { yylval.num = SADB_X_CALG_DEFLATE; return(ALG_COMP); }
+lzs { yylval.num = SADB_X_CALG_LZS; return(ALG_COMP); }
+{hyphen}R { return(F_RAWCPI); }
+
+ /* extension */
+{hyphen}m { return(F_MODE); }
+transport { yylval.num = IPSEC_MODE_TRANSPORT; return(MODE); }
+tunnel { yylval.num = IPSEC_MODE_TUNNEL; return(MODE); }
+{hyphen}u { return(F_REQID); }
+{hyphen}f { return(F_EXT); }
+random-pad { yylval.num = SADB_X_EXT_PRAND; return(EXTENSION); }
+seq-pad { yylval.num = SADB_X_EXT_PSEQ; return(EXTENSION); }
+zero-pad { yylval.num = SADB_X_EXT_PZERO; return(EXTENSION); }
+nocyclic-seq { return(NOCYCLICSEQ); }
+{hyphen}r { return(F_REPLAY); }
+{hyphen}lh { return(F_LIFETIME_HARD); }
+{hyphen}ls { return(F_LIFETIME_SOFT); }
+
+ /* ... */
+any { return(ANY); }
+{ws} { }
+{nl} { lineno++; }
+{comment}
+{semi} { return(EOT); }
+
+ /* for address parameters: /prefix, [port] */
+{slash} { return SLASH; }
+{blcl} { return BLCL; }
+{elcl} { return ELCL; }
+
+ /* parameter */
+{decstring} {
+ char *bp;
+
+ yylval.ulnum = strtoul(yytext, &bp, 10);
+ return(DECSTRING);
+ }
+
+{hexstring} {
+ yylval.val.buf = strdup(yytext + 2);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ yylval.val.len = strlen(yylval.val.buf);
+
+ return(HEXSTRING);
+ }
+
+{quotedstring} {
+ char *p = yytext;
+ while (*++p != '"') ;
+ *p = '\0';
+ yytext++;
+ yylval.val.len = yyleng - 2;
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+
+ return(QUOTEDSTRING);
+ }
+
+[A-Za-z0-9:][A-Za-z0-9:%\.-]* {
+ yylval.val.len = yyleng;
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ return(STRING);
+ }
+
+[0-9,]+ {
+ yylval.val.len = yyleng;
+ yylval.val.buf = strdup(yytext);
+ if (!yylval.val.buf)
+ yyfatal("insufficient memory");
+ return(STRING);
+ }
+
+. {
+ yyfatal("Syntax error");
+ /*NOTREACHED*/
+ }
+
+%%
+
+void
+yyfatal(s)
+ const char *s;
+{
+ yyerror(s);
+ exit(1);
+}
+
+void
+yyerror(s)
+ const char *s;
+{
+ printf("line %d: %s at [%s]\n", lineno, s, yytext);
+}
+
+int
+parse(fp)
+ FILE **fp;
+{
+ yyin = *fp;
+
+ parse_init();
+
+ if (yyparse()) {
+ printf("parse failed, line %d.\n", lineno);
+ return(-1);
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/setkey/vchar.h b/usr.sbin/setkey/vchar.h
new file mode 100644
index 0000000..f3251c7
--- /dev/null
+++ b/usr.sbin/setkey/vchar.h
@@ -0,0 +1,36 @@
+/* $FreeBSD$ */
+/* $KAME: vchar.h,v 1.2 2000/06/07 00:29:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+typedef struct {
+ u_int len;
+ caddr_t buf;
+} vchar_t;
diff --git a/usr.sbin/setpmac/Makefile b/usr.sbin/setpmac/Makefile
new file mode 100644
index 0000000..08b8b93
--- /dev/null
+++ b/usr.sbin/setpmac/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= setpmac
+MAN= setpmac.8
+SRCS= setpmac.c
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setpmac/setpmac.8 b/usr.sbin/setpmac/setpmac.8
new file mode 100644
index 0000000..345db64
--- /dev/null
+++ b/usr.sbin/setpmac/setpmac.8
@@ -0,0 +1,64 @@
+.\" 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..8314a77
--- /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
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "setpmac [label] [command] [args ...]\n");
+ exit (EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ 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..2f3a888
--- /dev/null
+++ b/usr.sbin/sicontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= sicontrol
+MAN= sicontrol.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sicontrol/sicontrol.8 b/usr.sbin/sicontrol/sicontrol.8
new file mode 100644
index 0000000..5693922
--- /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.
+.It Cm ttystat
+Show the current "tty" structure that the kernel has for the specified port.
+This is not much use outside of debugging the driver.
+.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..eb6560f
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.c
@@ -0,0 +1,572 @@
+/*
+ * 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 <peter@haywire.dialix.com>
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Andy Rutter of
+ * Advanced Methods and Tools Ltd. based on original information
+ * from Specialix International.
+ * 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>
+
+#include <dev/si/si.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 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 tty_stat(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},
+ {"ttystat", tty_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_TTY 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",
+ "ttystat\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;
+ struct stat st;
+
+ if (strchr(Devname, '/') == NULL) {
+ char *acp = malloc(6 + strlen(Devname));
+ strcpy(acp, _PATH_DEV);
+ strcat(acp, Devname);
+ Devname = acp;
+ }
+ if (stat(Devname, &st) < 0)
+ errx(1, "can't stat %s", Devname);
+ dev.sid_card = SI_CARD(minor(st.st_rdev));
+ dev.sid_port = SI_PORT(minor(st.st_rdev));
+ 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);
+}
+
+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\n", CCB.x_status); /* BYTE x_status - XON / XOFF status */
+ printf("\tc_status 0x%x\n", 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\n", 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\n", CCB.hi_mr1); /* BYTE hi_mr1 - mode 1 image */
+ printf("\thi_mr2 0x%x\n", CCB.hi_mr2); /* BYTE hi_mr2 - mode 2 image */
+ printf("\thi_csr 0x%x\n", CCB.hi_csr); /* BYTE hi_csr - clock register */
+ printf("\thi_op 0x%x\n", CCB.hi_op); /* BYTE hi_op - Op control */
+ printf("\thi_ip 0x%x\n", CCB.hi_ip); /* BYTE hi_ip - Input pins */
+ printf("\thi_state 0x%x\n", CCB.hi_state); /* BYTE hi_state - status */
+ printf("\thi_prtcl 0x%x\n", 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\n", 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] - */
+}
+
+void
+tty_stat(int ac, char **av)
+{
+ struct si_pstat sip;
+#define TTY sip.tc_tty
+
+ if (ac != 0)
+ prusage(U_STAT_TTY, 1);
+ sip.tc_dev = tc.tc_dev;
+ if (ioctl(ctlfd, TCSI_TTY, &sip) < 0)
+ err(1, "TCSI_TTY on %s", Devname);
+ printf("%s: ", Devname);
+
+ printf("\tt_outq.c_cc %d.\n", TTY.t_outq.c_cc); /* struct clist t_outq */
+ printf("\tt_dev 0x%x\n", TTY.t_dev); /* dev_t t_dev */
+ printf("\tt_flags 0x%x\n", TTY.t_flags); /* int t_flags */
+ printf("\tt_state 0x%x\n", TTY.t_state); /* int t_state */
+ printf("\tt_ihiwat %d.\n", TTY.t_ihiwat); /* int t_ihiwat */
+ printf("\tt_ilowat %d.\n", TTY.t_ilowat); /* int t_ilowat */
+ printf("\tt_ohiwat %d.\n", TTY.t_ohiwat); /* int t_ohiwat */
+ printf("\tt_olowat %d.\n", TTY.t_olowat); /* int t_olowat */
+ printf("\tt_iflag 0x%x\n", TTY.t_iflag); /* t_iflag */
+ printf("\tt_oflag 0x%x\n", TTY.t_oflag); /* t_oflag */
+ printf("\tt_cflag 0x%x\n", TTY.t_cflag); /* t_cflag */
+ printf("\tt_lflag 0x%x\n", TTY.t_lflag); /* t_lflag */
+ printf("\tt_cc %p\n", (void *)TTY.t_cc); /* t_cc */
+ printf("\tt_termios.c_ispeed 0x%x\n", TTY.t_termios.c_ispeed); /* t_termios.c_ispeed */
+ printf("\tt_termios.c_ospeed 0x%x\n", TTY.t_termios.c_ospeed); /* t_termios.c_ospeed */
+}
+
+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..860b9b0
--- /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
+INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sliplogin/pathnames.h b/usr.sbin/sliplogin/pathnames.h
new file mode 100644
index 0000000..3ed5d98
--- /dev/null
+++ b/usr.sbin/sliplogin/pathnames.h
@@ -0,0 +1,48 @@
+/*-
+ * 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. 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: @(#)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..994df95
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.8
@@ -0,0 +1,312 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)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 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 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 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..e1f8c7ef
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.c
@@ -0,0 +1,549 @@
+/*-
+ * 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. 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
+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);
+}
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, s, ldisc;
+ char *name;
+ struct termios tios, otios;
+ char logincmd[2*BUFSIZ+32];
+ extern uid_t getuid();
+
+ 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);
+ }
+ otios = tios;
+ 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..26e8345
--- /dev/null
+++ b/usr.sbin/slstat/slstat.8
@@ -0,0 +1,129 @@
+.\" 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.
+.\" 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: @(#)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 ststat 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..fbe52cf
--- /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_if.if_ibytes),
+ V(sc_if.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_if.if_ipackets) -
+ V(sc_comp.sls_compressedin) -
+ V(sc_comp.sls_uncompressedin) -
+ V(sc_comp.sls_errorin),
+ V(sc_if.if_ierrors));
+ printf(" | %8lu %6ld %6u %6u %6lu",
+ V(sc_if.if_obytes) / (rflag ? interval : 1),
+ V(sc_if.if_opackets),
+ V(sc_comp.sls_compressed),
+ V(sc_comp.sls_packets) - V(sc_comp.sls_compressed),
+ V(sc_if.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_if.if_oerrors),
+ V(sc_if.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..1fc7d3e
--- /dev/null
+++ b/usr.sbin/smbmsg/Makefile
@@ -0,0 +1,8 @@
+#
+# $FreeBSD$
+
+PROG= smbmsg
+MAN8= 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..f25da7b
--- /dev/null
+++ b/usr.sbin/smbmsg/smbmsg.8
@@ -0,0 +1,286 @@
+.\" 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
+command can be used to send or receive messages over an
+SMBus, see
+.Xr smbus 4 .
+.Pp
+The
+.Nm
+command 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:
+.Pp
+.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:
+.Pp
+.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 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 %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
+Exit status is 0 on success, or according to
+.Xr sysexits 3
+in case of failure.
+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
+command 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/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..e11e73e
--- /dev/null
+++ b/usr.sbin/sysinstall/Makefile
@@ -0,0 +1,98 @@
+# $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 tape.c tcpip.c termcap.c ttys.c ufs.c usb.c user.c \
+ variable.c ${_wizard} keymap.h
+
+.if ${MACHINE} == "pc98"
+SRCS+= pccard.c
+.endif
+
+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
+
+CLEANFILES= makedevs.c rtermcap
+CLEANFILES+= keymap.tmp keymap.h
+
+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 \
+ cs.latin2.qwertz danish.iso el.iso07 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 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 " { 0 }"; echo "};" ; echo "" ) >> keymap.tmp
+ mv keymap.tmp keymap.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sysinstall/anonFTP.c b/usr.sbin/sysinstall/anonFTP.c
new file mode 100644
index 0000000..7d130f1
--- /dev/null
+++ b/usr.sbin/sysinstall/anonFTP.c
@@ -0,0 +1,327 @@
+/*
+ * 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 5
+#define FTP_GROUP "operator"
+#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",
+ 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 },
+ { 0 },
+};
+
+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("14", "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"
+ "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 (!tconf.upload[0])
+ SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
+
+ /*** 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/bin && chmod 555 %s/bin", tconf.homedir, tconf.homedir);
+ vsystem("cp /bin/ls %s/bin && chmod 111 %s/bin/ls", tconf.homedir, tconf.homedir);
+ vsystem("cp /bin/date %s/bin && chmod 111 %s/bin/date", tconf.homedir, tconf.homedir);
+ vsystem("mkdir %s/etc && chmod 555 %s/etc", tconf.homedir, tconf.homedir);
+ vsystem("mkdir -p %s/pub", tconf.homedir);
+ 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 ($3 < 10 || $1 == \"ftp\") print $0}' /etc/passwd > %s/etc/passwd && chmod 444 %s/etc/passwd", tconf.homedir, tconf.homedir);
+ vsystem("awk -F: '{if ($3 < 100) print $0}' /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/cdrom.c b/usr.sbin/sysinstall/cdrom.c
new file mode 100644
index 0000000..b9658d0
--- /dev/null
+++ b/usr.sbin/sysinstall/cdrom.c
@@ -0,0 +1,212 @@
+/*
+ * 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";
+
+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;
+
+ if (cdromMounted)
+ return TRUE;
+
+ Mkdir(mountpoint);
+ bzero(&args, sizeof(args));
+ args.fspec = dev->devname;
+ args.flags = 0;
+ if (mount("cd9660", mountpoint, MNT_RDONLY, (caddr_t) &args) == -1) {
+ if (errno == EINVAL) {
+ msgConfirm("The disc in your drive looks more like an Audio disc than a FreeBSD release.");
+ return FALSE;
+ } else if (errno == EBUSY) {
+ /* Perhaps the CDROM drive is already mounted as /cdrom */
+ if (file_readable("/cdrom/cdrom.inf")) {
+ previouslyMounted = TRUE;
+ strlcpy(mountpoint, "/cdrom", 7);
+ }
+ } else {
+ 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") &&
+#ifdef __alpha__
+ strcmp(cp, "alpha")) {
+#elif 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..d055933
--- /dev/null
+++ b/usr.sbin/sysinstall/config.c
@@ -0,0 +1,1253 @@
+/*
+ * 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 <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 sysinstall then they may NOT\n"
+ "be found by this run!");
+ }
+ }
+
+ 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) {
+ if (c1->type == freebsd) {
+ 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.
+ */
+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
+ fprintf(rcSite, "#REMOVED: %s", line);
+ }
+ fclose(rcOld);
+ } else {
+ 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
+configUsers(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+#ifdef WITH_LINUX
+int
+configLinux(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+ int i;
+
+ dialog_clear_norefresh();
+ variable_set2(VAR_LINUX_ENABLE, "YES", 1);
+ Mkdir("/compat/linux");
+ msgNotify("Installing Linux compatibility library...");
+ i = package_add("linux_base");
+ restorescr(w);
+ return i;
+}
+#endif
+
+#ifdef __alpha__
+int
+configOSF1(dialogMenuItem *self)
+{
+
+ variable_set2(VAR_OSF1_ENABLE, "YES", 1);
+ Mkdir("/compat/osf1");
+ return DITEM_SUCCESS;
+}
+#endif
+
+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
+configSecurity(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuSecurity, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static void
+write_root_xprofile(char *str)
+{
+ FILE *fp;
+ int len;
+ char **cp;
+ static char *flist[] = { /* take care of both xdm and startx */
+ "/root/.xinitrc",
+ "/root/.xsession",
+ "/usr/share/skel/dot.xinitrc",
+ "/usr/share/skel/dot.xsession",
+ NULL,
+ };
+
+ len = strlen(str);
+ for (cp = flist; *cp; cp++) {
+ fp = fopen(*cp, "w");
+ if (fp) {
+ fwrite(str, 1, len, fp);
+ fchmod(fileno(fp), 0755);
+ fclose(fp);
+ }
+ }
+}
+
+static int
+gotit(char *fname)
+{
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, sizeof tmp, "/usr/X11R6/bin/%s", fname);
+ if (file_executable(tmp))
+ return TRUE;
+ snprintf(tmp, sizeof tmp, "/usr/local/bin/%s", fname);
+ return file_executable(tmp);
+}
+
+int
+configXDesktop(dialogMenuItem *self)
+{
+ char *desk;
+ int ret = DITEM_SUCCESS;
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXDesktops, FALSE) || !(desk = variable_get(VAR_DESKSTYLE))) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ if (!strcmp(desk, "kde")) {
+ ret = package_add("kde-lite");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("startkde"))
+ write_root_xprofile("exec startkde\n");
+ }
+ else if (!strcmp(desk, "gnome2")) {
+ ret = package_add("gnome2-lite");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session"))
+ write_root_xprofile("exec gnome-session\n");
+ }
+ else if (!strcmp(desk, "afterstep")) {
+ ret = package_add("afterstep");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("afterstep"))
+ write_root_xprofile("exec afterstep\n");
+ }
+ else if (!strcmp(desk, "windowmaker")) {
+ ret = package_add("windowmaker");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("wmaker.inst")) {
+ write_root_xprofile("xterm &\n[ ! -d $HOME/GNUstep/Library/WindowMaker ] && /usr/X11R6/bin/wmaker.inst\nexec /usr/X11R6/bin/wmaker\n");
+ }
+ }
+ else if (!strcmp(desk, "fvwm2")) {
+ ret = package_add("fvwm");
+ if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("fvwm"))
+ write_root_xprofile("exec fvwm\n");
+ }
+ if (DITEM_STATUS(ret) == DITEM_FAILURE)
+ msgConfirm("An error occurred while adding the package(s) required\n"
+ "by this desktop type. Please change installation media\n"
+ "and/or select a different, perhaps simpler, desktop\n"
+ "environment and try again.");
+ restorescr(w);
+ return ret;
+}
+
+int
+configXSetup(dialogMenuItem *self)
+{
+ char *config, *execfile, *execcmd, *style, *tmp;
+#ifdef WITH_MICE
+ char *moused;
+#endif
+ WINDOW *w = savescr();
+
+ setenv("XWINHOME", "/usr/X11R6", 1);
+tryagain:
+ variable_unset(VAR_DESKSTYLE);
+ variable_unset(VAR_XF86_CONFIG);
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE)) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ config = variable_get(VAR_XF86_CONFIG);
+ style = variable_get(VAR_DESKSTYLE);
+ if (!config) {
+ if (style)
+ goto config_desktop;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+
+ if (file_readable("/var/run/ld-elf.so.hints"))
+ vsystem("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ else
+ vsystem("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+
+ vsystem("/sbin/ifconfig lo0 127.0.0.1");
+
+ /*
+ * execcmd may have been passed in as a command name with
+ * arguments. Therefore, before determining if it is suitable for
+ * execution, we must split off the filename component from the
+ * command line arguments.
+ */
+
+ execcmd = string_concat("/usr/X11R6/bin/", config);
+ execfile = strdup(execcmd);
+ if ((tmp = strchr(execfile, ' ')))
+ *tmp = '\0';
+ if (file_executable(execfile)) {
+ free(execfile);
+#ifdef WITH_MICE
+ moused = variable_get(VAR_MOUSED);
+ while (!moused || strcmp(moused, "YES")) {
+ if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
+ "or indirect access via the mouse daemon. You have not\n"
+ "configured the mouse daemon. Would you like to configure it\n"
+ "now? If you intend to let the X server access the mouse\n"
+ "directly, choose \"No\" at this time."))
+ break;
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ moused = variable_get(VAR_MOUSED);
+ }
+ if (moused && !strcmp(moused, "YES"))
+ msgConfirm("You have configured and are now running the mouse daemon.\n"
+ "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
+ "\"MouseSystems\" as the mouse protocol in the X configuration\n"
+ "utility.");
+#endif
+ Mkdir("/etc/X11"); /* XXX:Remove this later after we are happy mtree will have created this for us. */
+ systemExecute(execcmd);
+ if (!file_readable("/etc/X11/XF86Config")) {
+ if (!msgYesNo("The XFree86 configuration process seems to have\nfailed. Would you like to try again?"))
+ goto tryagain;
+ else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+config_desktop:
+ configXDesktop(self);
+ restorescr(w);
+ return DITEM_SUCCESS;
+ }
+ else {
+ free(execfile);
+ msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
+ "Please install this before attempting to configure XFree86.");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+}
+
+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.%s localhost\n", dp);
+ fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\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;
+
+ /* 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;
+ for (tmp = Plist.kids; tmp && tmp->name; tmp = tmp->next)
+ (void)index_extract(mediaDevice, &Top, tmp, FALSE);
+ 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/ports 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 synatx 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)
+{
+ int retval = 0;
+ int doupdate = 1;
+
+ if (self != NULL) {
+ retval = dmenuToggleVariable(self);
+ if (strcmp(variable_get(self->data), "YES") != 0)
+ doupdate = 0;
+ }
+
+ 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");
+ 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..e638201
--- /dev/null
+++ b/usr.sbin/sysinstall/devices.c
@@ -0,0 +1,582 @@
+/*
+ * 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>
+
+/* 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;
+
+static struct _devname {
+ DeviceType type;
+ char *name;
+ char *description;
+ int major, minor, delta, max;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd%d", "SCSI CDROM drive", 15, 2, 8, 4 },
+ { DEVICE_TYPE_CDROM, "mcd%d", "Mitsumi (old model) CDROM drive", 29, 0, 8, 4 },
+ { DEVICE_TYPE_CDROM, "scd%d", "Sony CDROM drive - CDU31/33A type", 45, 0, 8, 4 },
+#ifdef notdef
+ { DEVICE_TYPE_CDROM, "matcd%d", "Matsushita CDROM ('sound blaster' type)", 46, 0, 8, 4 },
+#endif
+ { DEVICE_TYPE_CDROM, "acd%d", "ATAPI/IDE CDROM", 117, 0, 8, 4 },
+ { DEVICE_TYPE_TAPE, "rsa%d", "SCSI tape drive", 14, 0, 16, 4 },
+ { DEVICE_TYPE_TAPE, "rwt%d", "Wangtek tape drive", 10, 0, 1, 4 },
+ { DEVICE_TYPE_DISK, "da%d", "SCSI disk device", 13, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ad%d", "ATA/IDE disk device", 116, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "ar%d", "ATA/IDE RAID device", 157, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "fla%d", "M-Systems DiskOnChip Flash devicee", 102, 65538, 8, 16 },
+ { DEVICE_TYPE_DISK, "afd%d", "ATAPI/IDE floppy device", 118, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "mlxd%d", "Mylex RAID disk", 131, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "amrd%d", "AMI MegaRAID drive", 133, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "idad%d", "Compaq RAID array", 109, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "twed%d", "3ware ATA RAID array", 147, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "aacd%d", "Adaptec FSA RAID array", 151, 65538, 8, 4 },
+ { DEVICE_TYPE_DISK, "ipsd%d", "IBM ServeRAID RAID array", 176, 65538, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 9, 0, 64, 4 },
+ { DEVICE_TYPE_NETWORK, "an", "Aironet 4500/4800 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "aue", "ADMtek USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "axe", "ASIX Electronics USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "bfe", "Broadcom BCM440x PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "bge", "Broadcom BCM570x PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cue", "CATC USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "fpa", "DEC DEFPA PCI FDDI card" },
+ { DEVICE_TYPE_NETWORK, "sr", "SDL T1/E1 sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "cc3i", "SDL HSSI sync serial PCI card" },
+ { DEVICE_TYPE_NETWORK, "en", "Efficient Networks ATM PCI card" },
+ { DEVICE_TYPE_NETWORK, "dc", "DEC/Intel 21143 (and clones) PCI fast ethernet card" },
+ { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" },
+ { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card/3C589 PCMCIA" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "em", "Intel(R) PRO/1000 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ex", "Intel EtherExpress Pro/10 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "fe", "Fujitsu MB86960A/MB86965A ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210" },
+ { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" },
+ { DEVICE_TYPE_NETWORK, "kue", "Kawasaki LSI USB ethernet adapter" },
+ { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "lge", "Level 1 LXT1001 gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "nge", "NatSemi PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "pcn", "AMD Am79c79x PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ray", "Raytheon Raylink 802.11 wireless adaptor" },
+ { DEVICE_TYPE_NETWORK, "re", "RealTek 8139C+/8169/8169S/8110S PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "rue", "RealTek USB ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sf", "Adaptec AIC-6915 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sis", "SiS 900/SiS 7016 PCI ethernet card" },
+#ifdef PC98
+ { DEVICE_TYPE_NETWORK, "snc", "SONIC ethernet card" },
+#endif
+ { DEVICE_TYPE_NETWORK, "sn", "SMC/Megahertz ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ste", "Sundance ST201 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "sk", "SysKonnect PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX ethernet card" },
+ { DEVICE_TYPE_NETWORK, "txp", "3Com 3cR990 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ti", "Alteon Networks PCI gigabit ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tl", "Texas Instruments ThunderLAN PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vr", "VIA VT3043/VT86C100A Rhine PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "vlan", "IEEE 802.1Q VLAN network interface" },
+ { DEVICE_TYPE_NETWORK, "vx", "3COM 3c590 / 3c595 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wb", "Winbond W89C840F PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wi", "Lucent WaveLAN/IEEE 802.11 wireless adapter" },
+ { DEVICE_TYPE_NETWORK, "wx", "Intel Gigabit Ethernet (82452) card" },
+ { DEVICE_TYPE_NETWORK, "xe", "Xircom/Intel EtherExpress Pro100/16 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "xl", "3COM 3c90x / 3c90xB PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cuaa%d", "%s on device %s (COM%d)", 28, 128, 1, 16 },
+ { DEVICE_TYPE_NETWORK, "fwe", "FireWire Ethernet emulation" },
+ { DEVICE_TYPE_NETWORK, "lp", "Parallel Port IP (PLIP) peer connection" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+ { DEVICE_TYPE_NETWORK, "disc", "Software discard network interface" },
+#ifdef PC98
+ { DEVICE_TYPE_DISK, "wd%d", "IDE disk device", 3, 65538, 8, 16 },
+ { DEVICE_TYPE_CDROM, "wcd%dc", "ATAPI IDE CDROM", 69, 2, 8, 4 },
+ { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 87, 0, 8, 4 },
+ { DEVICE_TYPE_DISK, "wfd%d", "ATAPI floppy device", 87, 65538, 8, 4 },
+#endif
+ { 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];
+ mode_t m;
+ dev_t d;
+ int fail;
+
+ 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);
+ return fd;
+ }
+ m = 0640 | S_IFCHR;
+ d = makedev(dev.major, dev.minor + (i * dev.delta));
+ if (isDebug())
+ msgDebug("deviceTry: Making %s device for %s [%d, %d]\n", m & S_IFCHR ? "raw" : "block", try, dev.major, dev.minor + (i * dev.delta));
+ fail = mknod(try, m, d);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded on second try.\n", try);
+ return fd;
+ }
+ else if (!fail)
+ (void)unlink(try);
+ /* Don't try a "make-under" here since we're using a fixit floppy in this case */
+ snprintf(try, FILENAME_MAX, "/mnt/dev/%s", unit);
+ fd = open(try, O_RDONLY);
+ if (isDebug())
+ msgDebug("deviceTry: final attempt for %s returns %d\n", try, fd);
+ 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_TAPE:
+ 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_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
+ if (isDebug())
+ msgDebug("Found a TAPE device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_DISK:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0 && RunningAsInit) {
+ dev_t d;
+ mode_t m;
+ int s, fail;
+ char unit[80], slice[80];
+
+ close(fd);
+ /* Make associated slice entries */
+ for (s = 1; s < 8; s++) {
+ snprintf(unit, sizeof unit, device_names[i].name, j);
+ snprintf(slice, sizeof slice, "/dev/%ss%d", unit, s);
+ d = makedev(device_names[i].major, device_names[i].minor +
+ (j * device_names[i].delta) + (s * SLICE_DELTA));
+ m = 0640 | S_IFCHR;
+ fail = mknod(slice, m, d);
+ fd = open(slice, O_RDONLY);
+ if (fd >= 0)
+ close(fd);
+ else if (!fail)
+ (void)unlink(slice);
+ }
+ }
+ 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;
+
+ 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..535fb05
--- /dev/null
+++ b/usr.sbin/sysinstall/disks.c
@@ -0,0 +1,996 @@
+/*
+ * 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 <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 */
+static struct chunk *chunk_info[16];
+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
+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;
+#ifdef PC98
+ if (d->bios_cyl >= 65536 || d->bios_hd > 16 || d->bios_sect >= 256) {
+#else
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+#endif
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %lu/%lu/%lu for %s is incorrect. Using\n"
+ "a more likely geometry. If this geometry is incorrect or you\n"
+ "are unsure as to whether or not it's correct, please consult\n"
+ "the Hardware Guide in the Documentation submenu or use the\n"
+ "(G)eometry command to change it now.\n\n"
+ "Remember: you need to enter whatever your BIOS thinks the\n"
+ "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''.",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ }
+ 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()
+{
+ 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;
+ 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);
+
+ 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__) && !defined(__ia64__)
+ 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':
+ /* 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;
+
+#ifndef __ia64__
+ 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;
+#endif
+
+ 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(__ia64__) || 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) {
+ 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..ee9fea4
--- /dev/null
+++ b/usr.sbin/sysinstall/dispatch.c
@@ -0,0 +1,442 @@
+/*
+ * 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_mediaClose(dialogMenuItem *unused);
+
+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 },
+ { "configXSetup", configXSetup },
+ { "configXDesktop", configXDesktop },
+#ifdef WITH_SLICES
+ { "diskPartitionEditor", diskPartitionEditor },
+#endif
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distUnsetCustom", distUnsetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "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 },
+ { "mediaClose", dispatch_mediaClose },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "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_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;
+ }
+ /* 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;
+}
+
+
+/*
+ * 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)) {
+
+ if ((cp = strchr(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\n"
+ "residing on a MSDOS or UFS floppy.", 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;
+}
+
diff --git a/usr.sbin/sysinstall/dist.c b/usr.sbin/sysinstall/dist.c
new file mode 100644
index 0000000..287f030
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.c
@@ -0,0 +1,875 @@
+/*
+ * 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 <signal.h>
+#include <libutil.h>
+
+unsigned int Dists;
+unsigned int CRYPTODists;
+unsigned int SrcDists;
+unsigned int XF86Dists;
+unsigned int XF86ServerDists;
+unsigned int XF86FontDists;
+
+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;
+
+extern Distribution DistTable[];
+extern Distribution CRYPTODistTable[];
+extern Distribution SrcDistTable[];
+extern Distribution XF86DistTable[];
+extern Distribution XF86FontDistTable[];
+extern Distribution XF86ServerDistTable[];
+
+#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 BASE_DIST (&DistTable[0])
+
+/* The top-level distribution categories */
+static Distribution DistTable[] = {
+ DTE_TARBALL("base", &Dists, BASE, "/"),
+ 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, "/"),
+ DTE_SUBDIST("src", &Dists, SRC, SrcDistTable),
+ DTE_SUBDIST("crypto", &Dists, CRYPTO, CRYPTODistTable),
+#ifdef __i386__
+ DTE_TARBALL("compat1x", &Dists, COMPAT1X, "/"),
+ DTE_TARBALL("compat20", &Dists, COMPAT20, "/"),
+ DTE_TARBALL("compat21", &Dists, COMPAT21, "/"),
+ DTE_TARBALL("compat22", &Dists, COMPAT22, "/"),
+ DTE_TARBALL("compat3x", &Dists, COMPAT3X, "/"),
+#endif
+#if defined(__i386__) || defined(__alpha__)
+ DTE_TARBALL("compat4x", &Dists, COMPAT4X, "/"),
+#endif
+ DTE_TARBALL("ports", &Dists, PORTS, "/usr"),
+ DTE_TARBALL("local", &Dists, LOCAL, "/"),
+ DTE_PACKAGE("perl", &Dists, PERL, "perl"),
+ DTE_SUBDIST("XFree86", &Dists, XF86, XF86DistTable),
+ { NULL },
+};
+
+/* The CRYPTO distribution */
+static Distribution CRYPTODistTable[] = {
+ DTE_TARBALL("crypto", &CRYPTODists, CRYPTO_CRYPTO, "/"),
+ DTE_TARBALL("ssecure", &CRYPTODists, CRYPTO_SSECURE, "/usr/src"),
+ DTE_TARBALL("scrypto", &CRYPTODists, CRYPTO_SCRYPTO, "/usr/src"),
+ DTE_TARBALL("skrb5", &CRYPTODists, CRYPTO_SKERBEROS5, "/usr/src"),
+ { NULL },
+};
+
+/* The /usr/src distribution */
+static Distribution SrcDistTable[] = {
+ DTE_TARBALL("sbase", &SrcDists, SRC_BASE, "/usr/src"),
+ DTE_TARBALL("scontrib", &SrcDists, SRC_CONTRIB, "/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("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("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"),
+ { NULL },
+};
+
+/* The XFree86 distribution */
+static Distribution XF86DistTable[] = {
+ DTE_SUBDIST("XFree86", &XF86Dists, XF86_FONTS, XF86FontDistTable),
+ DTE_SUBDIST("XFree86", &XF86Dists, XF86_SERVER, XF86ServerDistTable),
+ DTE_PACKAGE("Xbin", &XF86Dists, XF86_CLIENTS, "XFree86-clients"),
+ DTE_PACKAGE("Xdoc", &XF86Dists, XF86_DOC, "XFree86-documents"),
+ DTE_PACKAGE("Xlib", &XF86Dists, XF86_LIB, "XFree86-libraries"),
+ DTE_PACKAGE("Xman", &XF86Dists, XF86_MAN, "XFree86-manuals"),
+ DTE_PACKAGE("Xprog", &XF86Dists, XF86_PROG, "imake"),
+ { NULL },
+};
+
+/* The XFree86 server distribution */
+static Distribution XF86ServerDistTable[] = {
+ DTE_PACKAGE("Xsrv", &XF86ServerDists, XF86_SERVER_FB, "wrapper"),
+ DTE_PACKAGE("Xnest", &XF86ServerDists, XF86_SERVER_NEST, "XFree86-NestServer"),
+ DTE_PACKAGE("Xprt", &XF86ServerDists, XF86_SERVER_PRINT, "XFree86-PrintServer"),
+ DTE_PACKAGE("Xvfb", &XF86ServerDists, XF86_SERVER_VFB, "XFree86-VirtualFramebufferServer"),
+ { NULL }
+};
+
+/* The XFree86 font distribution */
+static Distribution XF86FontDistTable[] = {
+ DTE_PACKAGE("Xf75", &XF86FontDists, XF86_FONTS_75, "XFree86-font75dpi"),
+ DTE_PACKAGE("Xf100", &XF86FontDists, XF86_FONTS_100, "XFree86-font100dpi"),
+ DTE_PACKAGE("Xfcyr", &XF86FontDists, XF86_FONTS_CYR, "XFree86-fontCyrillic"),
+ DTE_PACKAGE("Xfscl", &XF86FontDists, XF86_FONTS_SCALE, "XFree86-fontScalable"),
+ DTE_PACKAGE("Xfnts", &XF86FontDists, XF86_FONTS_BITMAPS, "XFree86-fontDefaultBitmaps"),
+ DTE_PACKAGE("Xfsrv", &XF86FontDists, XF86_FONTS_SERVER, "XFree86-FontServer"),
+ { NULL },
+};
+
+static int distMaybeSetPorts(dialogMenuItem *self);
+
+static void
+distVerifyFlags(void)
+{
+ if (SrcDists)
+ Dists |= DIST_SRC;
+ if (CRYPTODists)
+ Dists |= DIST_CRYPTO;
+ else if ((Dists & DIST_CRYPTO) && !CRYPTODists)
+ CRYPTODists |= DIST_CRYPTO_ALL;
+ if (XF86ServerDists)
+ XF86Dists |= DIST_XF86_SERVER;
+ if (XF86FontDists)
+ XF86Dists |= DIST_XF86_FONTS;
+ if (XF86Dists || XF86ServerDists || XF86FontDists)
+ Dists |= DIST_XF86;
+ if (isDebug()) {
+ msgDebug("Dist Masks: Dists: %0x, CRYPTO: %0x, Srcs: %0x\n", Dists,
+ CRYPTODists, SrcDists);
+ msgDebug("XServer: %0x, XFonts: %0x, XDists: %0x\n", XF86ServerDists,
+ XF86FontDists, XF86Dists);
+ }
+}
+
+int
+distReset(dialogMenuItem *self)
+{
+ Dists = 0;
+ CRYPTODists = 0;
+ SrcDists = 0;
+ XF86Dists = 0;
+ XF86ServerDists = 0;
+ XF86FontDists = 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_CRYPTO)) != NULL)
+ CRYPTODists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_SRC)) != NULL)
+ SrcDists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_X11)) != NULL)
+ XF86Dists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_XSERVER)) != NULL)
+ XF86ServerDists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_XFONTS)) != NULL)
+ XF86FontDists = atoi(cp);
+ distVerifyFlags();
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+distSetX(void)
+{
+ Dists |= DIST_XF86;
+ XF86Dists = DIST_XF86_CLIENTS | DIST_XF86_LIB | DIST_XF86_PROG | DIST_XF86_MAN | DIST_XF86_DOC | DIST_XF86_SERVER | DIST_XF86_FONTS;
+ XF86ServerDists = DIST_XF86_SERVER_FB;
+ XF86FontDists = DIST_XF86_FONTS_BITMAPS | DIST_XF86_FONTS_75 | DIST_XF86_FONTS_100;
+ return distSetXF86(NULL);
+}
+
+int
+distSetDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_ALL;
+ CRYPTODists = DIST_CRYPTO_ALL;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ i = distSetDeveloper(self);
+ i |= distSetX();
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetKernDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_SYS;
+ CRYPTODists |= DIST_CRYPTO_CRYPTO;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXKernDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ i = distSetKernDeveloper(self);
+ i |= distSetX();
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetUser(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_USER;
+ CRYPTODists |= DIST_CRYPTO_CRYPTO;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXUser(dialogMenuItem *self)
+{
+ int i;
+
+ i = distSetUser(self);
+ i |= distSetX();
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetMinimum(dialogMenuItem *self)
+{
+ distReset(NULL);
+ Dists = DIST_BASE | DIST_CRYPTO;
+ CRYPTODists |= DIST_CRYPTO_CRYPTO;
+ distVerifyFlags();
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distSetEverything(dialogMenuItem *self)
+{
+ int i;
+
+ Dists = DIST_ALL | DIST_XF86;
+ SrcDists = DIST_SRC_ALL;
+ CRYPTODists = DIST_CRYPTO_ALL;
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+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 10500 ported software packages,\n"
+ "at a cost of around 300MB 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
+distSetXF86(dialogMenuItem *self)
+{
+ int i = DITEM_SUCCESS;
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Select, FALSE))
+ i = DITEM_FAILURE;
+ 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;
+}
+
+/*
+ * 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, 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;
+
+ status = TRUE;
+ numchunks = 0;
+ snprintf(fname, sizeof (fname), "%s/%s.inf", path, dist);
+
+getinfo:
+ fp = DEVICE_GET(mediaDevice, fname, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || 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, dist,
+ USE_GZIP ? "tgz" : "tbz");
+ /*
+ * 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) {
+ /* 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
+ 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) {
+ 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)
+ 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, dist, (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, 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;
+ 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);
+
+ /* 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..13265f5
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.h
@@ -0,0 +1,89 @@
+/* $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
+#ifdef __i386__ /* only applicable on x86 */
+#define DIST_COMPAT1X 0x00100
+#define DIST_COMPAT20 0x00200
+#define DIST_COMPAT21 0x00400
+#define DIST_COMPAT22 0x00800
+#define DIST_COMPAT3X 0x01000
+#endif
+#if defined(__i386__) || defined(__alpha__)
+#define DIST_COMPAT4X 0x02000
+#endif
+#define DIST_XF86 0x04000
+#define DIST_CRYPTO 0x08000
+#define DIST_CATPAGES 0x10000
+#define DIST_PORTS 0x20000
+#define DIST_LOCAL 0x40000
+#define DIST_PERL 0x80000
+#define DIST_ALL 0xFFFFF
+
+/* Subtypes for CRYPTO distribution */
+#define DIST_CRYPTO_CRYPTO 0x0001
+#define DIST_CRYPTO_SCRYPTO 0x0002
+#define DIST_CRYPTO_SSECURE 0x0004
+#define DIST_CRYPTO_SKERBEROS5 0x0008
+#define DIST_CRYPTO_ALL 0x000F
+
+/* 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_ALL 0x0FFFF
+
+/* Subtypes for XFree86 packages */
+#define DIST_XF86_CLIENTS 0x0001
+#define DIST_XF86_DOC 0x0002
+#define DIST_XF86_LIB 0x0004
+#define DIST_XF86_MAN 0x0008
+#define DIST_XF86_PROG 0x0010
+#define DIST_XF86_MISC_ALL 0x001F
+#define DIST_XF86_SERVER 0x0020
+#define DIST_XF86_SERVER_FB 0x0001
+#define DIST_XF86_SERVER_NEST 0x0002
+#define DIST_XF86_SERVER_PRINT 0x0004
+#define DIST_XF86_SERVER_VFB 0x0008
+#define DIST_XF86_SERVER_ALL 0x000F
+#define DIST_XF86_FONTS 0x0040
+#define DIST_XF86_FONTS_75 0x0001
+#define DIST_XF86_FONTS_100 0x0002
+#define DIST_XF86_FONTS_CYR 0x0004
+#define DIST_XF86_FONTS_SCALE 0x0008
+#define DIST_XF86_FONTS_BITMAPS 0x0010
+#define DIST_XF86_FONTS_SERVER 0x0020
+#define DIST_XF86_FONTS_ALL 0x003F
+#define DIST_XF86_ALL 0x007F
+
+/* Canned distribution sets */
+#define _DIST_USER \
+ ( DIST_BASE | DIST_DOC | DIST_MANPAGES | DIST_DICT | DIST_CRYPTO | DIST_PERL )
+
+#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..d89f8ce
--- /dev/null
+++ b/usr.sbin/sysinstall/dmenu.c
@@ -0,0 +1,319 @@
+/*
+ * 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 <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
+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, *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 (*((unsigned 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;
+}
+
+/* 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..fc17459
--- /dev/null
+++ b/usr.sbin/sysinstall/floppy.c
@@ -0,0 +1,189 @@
+/*
+ * 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;
+#ifdef PC98
+ char fddev[24];
+#endif
+
+ 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);
+#ifdef PC98
+ dosargs.fspec = fddev;
+#else
+ dosargs.fspec = dev->devname;
+#endif
+ dosargs.uid = dosargs.gid = 0;
+ dosargs.mask = 0777;
+
+ memset(&u_args, 0, sizeof(u_args));
+#ifdef PC98
+ u_args.fspec = fddev;
+#else
+ u_args.fspec = dev->devname;
+#endif
+
+#ifdef PC98
+ snprintf(fddev, sizeof (fddev), "%s.1200", 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;
+
+ snprintf(fddev, sizeof (fddev), "%s.1232", 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;
+
+ snprintf(fddev, sizeof (fddev), "%s.1440", 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;
+#else
+ 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;
+#endif /* PC98 */
+
+ 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..c971b79
--- /dev/null
+++ b/usr.sbin/sysinstall/ftp.c
@@ -0,0 +1,288 @@
+/*
+ * 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. */
+#ifdef PC98
+const char *ftp_dirs[] = { ".", "releases/pc98", "snapshots/pc98",
+ "pub/FreeBSD", "pub/FreeBSD/releases/pc98",
+ "pub/FreeBSD/snapshots/pc98", NULL };
+#else
+const char *ftp_dirs[] = { ".", "releases/"MACHINE, "snapshots/"MACHINE,
+ "pub/FreeBSD", "pub/FreeBSD/releases/"MACHINE,
+ "pub/FreeBSD/snapshots/"MACHINE, NULL };
+#endif
+
+/* 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..4323bd0
--- /dev/null
+++ b/usr.sbin/sysinstall/globals.c
@@ -0,0 +1,73 @@
+/*
+ * 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? */
+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 */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+/*
+ * 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;
+}
diff --git a/usr.sbin/sysinstall/help/anonftp.hlp b/usr.sbin/sysinstall/help/anonftp.hlp
new file mode 100644
index 0000000..e90985e
--- /dev/null
+++ b/usr.sbin/sysinstall/help/anonftp.hlp
@@ -0,0 +1,19 @@
+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.
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..53b691f
--- /dev/null
+++ b/usr.sbin/sysinstall/help/distributions.hlp
@@ -0,0 +1,42 @@
+DISTRIBUTION INFORMATION
+------------------------
+
+An ``X-'' prefixed before a distribution set means that the XFree86
+base distribution, libraries, manual pages, SVGA server 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.
+
+N.B. All references in this document to `complete source' mean the
+complete source tree minus any legally encumbered cryptography code.
+
+The current "canned" installations are provided:
+
+Developer: Base ("bin") distribution, man pages, dictionary
+ files, profiling libraries and the complete source tree.
+
+Kern-Developer: As above, but with only kernel sources instead of
+ the complete source tree.
+
+User: The base distribution, man pages, dictionary files and
+ the FreeBSD 1.x, 2.0, 2.1.x, 2.2.x and 3.x compatibility sets.
+
+Minimal: Only the base distribution.
+
+Everything: The base distribution, man pages, dictionary files,
+ profiling libraries, the FreeBSD compatibility libraries,
+ the complete source tree, games and your choice of XFree86
+ distribution components.
+
+ Note that the cryptography source code is NOT included
+ in this collection. You will need to select that by
+ hand if you're inside the United States.
+
+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..e47394e
--- /dev/null
+++ b/usr.sbin/sysinstall/help/fixit.hlp
@@ -0,0 +1,7 @@
+A special shell will be launched by this option with a fixit floppy
+(or 2nd CDROM) mounted as /mnt2. This provides access to extra
+commands under /mnt2 as well as a more complete set of device files in
+/mnt2/dev. Some device operations, like fsck and disklabel, may
+therefore require you to go to /mnt2/dev and use the entries there
+rather than assuming that they will be present in the default /dev
+(which came from the boot floppy and is very minimal).
diff --git a/usr.sbin/sysinstall/help/html.hlp b/usr.sbin/sysinstall/help/html.hlp
new file mode 100644
index 0000000..e6f22c4
--- /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..57ab36f
--- /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 driver 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..a65467b
--- /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 menu on the boot floppy). 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! FreeBSD 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..1f32172
--- /dev/null
+++ b/usr.sbin/sysinstall/help/options.hlp
@@ -0,0 +1,124 @@
+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.
+
+
+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!
+
+
+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.
+
+
+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.
+
+
+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 bin distribution is also installed (usually
+ the case anyway) since /usr/sbin/pkg_add will otherwise not be
+ found after the chroot() call.
+
+
+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).
+
+
+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
+ hence a small /tmp) then you may wish to set this to point at another
+ location (say, /usr/tmp).
+
+
+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..1d62148
--- /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 80MB or more Yes
+
+Note: If you do not create a /usr filesystem then your / filesystem
+will need to be bigger - at least 200MB. 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/register.hlp b/usr.sbin/sysinstall/help/register.hlp
new file mode 100644
index 0000000..811f71d
--- /dev/null
+++ b/usr.sbin/sysinstall/help/register.hlp
@@ -0,0 +1,76 @@
+This screen allows you to register yourself with the FreeBSD Project's
+user counter & statistics database.
+
+** IT IS VERY IMPORTANT THAT YOU DO THIS! **
+
+Believe me, I hate filling out forms as much as anyone, and most
+people's understandable reaction to a registration form is to say "Eh,
+what's this? They want to send me junk mail and then on top of that
+they expect me to go to *extra* trouble in order to make it easy for
+them?! Forget it!"
+
+This is not that kind of registration, and I strongly urge you to take
+just a few minutes to read this and find out how much the simple act
+of registering can help both you and FreeBSD.
+
+1. It is very much in your best interest, as a FreeBSD user, to stand
+ up and be counted so that various software vendors will begin to
+ take you and your operating system seriously. There are numerous
+ ISVs (Independent Software Vendors) who would be only too happy
+ to port the kinds of applications that many FreeBSD users are
+ currently screaming for (everything from spreadsheets and word
+ processing packages to games) if they only had some idea that it
+ might be worth the trouble. The only way to convince the ISVs that
+ FreeBSD is worth their trouble is to show them how many users
+ we have, and to do that we need your registration! At this time
+ we literally do not know how many users FreeBSD has, and that's
+ hardly helpful when you're trying to convince someone to port
+ software to it.
+
+
+2. We will not send you *anything* you do not ask for. Some people
+ are genuinely interested in new product announcements for FreeBSD
+ or want to hear about security issues & other important advisories
+ as they come up, and for such people we've added registration
+ options for selecting various types of additional material they
+ might be interested in receiving as a side-effect of registration.
+
+ The default behavior is to NOT put the user on any special mailing
+ lists or provide their names in mailing list data sent to
+ (carefully screened) FreeBSD product advertisers - all of that must
+ be specifically requested during the registration.
+
+Most fields in the form are fairly self-explanatory. At the minimum,
+you should enter your first and last name as well as your email
+address so that we can weed obvious duplicates from the counter. You
+will NOT be sent any mail at this address unless you also sign up for
+one of the additional notification services, and it's only used to
+provide us with a way of differentiating "John Smith <smith@foo.org>"
+from "John Smith <jsm@bar.com>" in the simple, no-frills registration
+case. If you do not have an email address, some sort of postal
+address will serve the same purpose.
+
+If you also wish to receive the FreeBSD Newsletter, published and
+distributed free of charge by Walnut Creek CDROM in printed form,
+then you must specify some sort of postal address. Likewise, if you
+elect to receive notification on the email version then you should
+specify a valid Email address. Back-issues of the FreeBSD newsletter
+are available at ftp://ftp.freebsd.org/pub/FreeBSD/doc/newsletter/ .
+
+Should you wish to unsubscribe to the FreeBSD Newsletter or otherwise
+de-register yourself at a later time, you can simply send mail to
+register-request@freebsd.org. If you subscribe to the announce
+mailing list (and it's a good idea) then you can modify your
+subscription at any time by sending mail to majordomo@freebsd.org
+
+
+Your cooperation with this new registration service is greatly
+appreciated, and by taking just 5 minutes to fill this out now you
+will be helping us to gather data which will greatly assist FreeBSD in
+firmly establishing a position as a serious UN*X operating system
+contender.
+
+Regards,
+
+ Jordan Hubbard,
+ FreeBSD PR Officer
diff --git a/usr.sbin/sysinstall/help/securelevel.hlp b/usr.sbin/sysinstall/help/securelevel.hlp
new file mode 100644
index 0000000..c0964e4
--- /dev/null
+++ b/usr.sbin/sysinstall/help/securelevel.hlp
@@ -0,0 +1,38 @@
+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 init(8) manual page 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, and /dev/kmem 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) and ipfirewall(4)) cannot be
+ changed and dummynet(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..8964ead
--- /dev/null
+++ b/usr.sbin/sysinstall/help/shortcuts.hlp
@@ -0,0 +1,114 @@
+/stand/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:
+
+/stand/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?
+disk Which disk to operate on (wd0, da0, etc).
+domainname Domain name
+editor Which screen editor to use
+ifconfig_<iface> For each <iface> in network_devices
+ftpDirectory Root of the FreeBSD distribution tree on FTP server
+ftpOnError Set to retry or abort
+ftpPass Which password to use when logging into FTP server
+ftp Which FTP site/dir to use (URL ftp://site/dir/..)
+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
+ftpHost Which FTP hostname to use (overrides ftp variable)
+gated Use gated instead of routed
+defaultrouter IP address of default route
+geometry Geometry to use for selected disk ("cyl/hd/sec")
+hostname Fully qualified domain name for host.
+network_interfaces Which network interfaces to configure
+ipaddr IP address for this host's primary interface
+nameserver IP address of name server
+netmask Netmask for this host's primary interface
+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:
+
+addUser Add a new user to the system
+addGroup Add a new group to the system
+configAnonFTP Configure system for anonymous FTP
+configGated Configure and install gated
+configNFSServer Configure host as an NFS server
+configPackages Browse / install packages
+diskPartitionEditor Partition a new or existing disk
+diskPartitionWrite Write out any changed partition information
+diskLabelEditor Label/Newfs/Mount new or existing filesystems
+diskLabelCommit Write out any changed label information
+distReset Reset distribution information
+distSetDeveloper Select developer distribution
+distSetXDeveloper Select X developer distribution
+distSetKernDeveloper Select kernel developer distribution
+distSetUser Select user distribution
+distSetXUser Select X user distribution
+distSetMinimum Select minimal distribution
+distSetEverything Select all distributions
+distSetDES Select DES sub-distributions
+distSetSrc Select source sub-distributions
+distSetXF86 Select XFree86 sub-distributions
+distExtractAll Extract all selected distributions
+docBrowser Browse documentation
+installCommit Commit any pending installation operations
+installExpress Express installation
+installUpgrade Upgrade installation
+installFixup Go into "fixit" mode
+mediaSetCDROM Select CDROM media
+mediaSetFloppy Select floppy media
+mediaSetDOS Select DOS media
+mediaSetTape Select tape media
+mediaSetFTP Select FTP media
+mediaSetFTPPassive Select FTP media in passive mode
+mediaSetUFS Select UFS media
+mediaSetNFS Select NFS media
+mediaSetFtpUserPass Prompt for FTP username and password
+mediaSetCPIOVerbosity Prompt for CPIO verbosity
+mediaGetType Prompt for media type
+optionsEditor Go to options editor
+register Go to registration editor.
+
+Examples:
+
+/stand/sysinstall mediaSetFTP configPackages
+
+Selects an FTP site and then goes to the package configuration menu.
+
+
+/stand/sysinstall disk=da0 diskPartitionEditor
+
+Invokes the disk partition editor on disk da0.
+
+
+If /stand/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..b6f6a83
--- /dev/null
+++ b/usr.sbin/sysinstall/help/slice.hlp
@@ -0,0 +1,65 @@
+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'. 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. If you select the default of `Yes', 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, you can select `No' at the
+compatibility prompt. 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'.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ '>' -- This slice doesn't end before cylinder 1024
+ 'R' -- This slice contains the root (/) filesystem
+ 'B' -- Slice employs BAD144 bad-spot handling
+ 'C' -- This is the FreeBSD 2.0-compatibility slice (default)
+ 'A' -- This slice is marked active.
+
+If you select a slice for Bad144 handling, it will be scanned
+for bad blocks before any new filesystems are made on it.
+
+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'.
+
+Final Note: If you're absolutely sure you know what you're doing
+ and you want to use the old "Dangerously Dedicated" mode
+ which is now deprecated by sysinstall, use the (purposely)
+ undocumented `F' key.
+
diff --git a/usr.sbin/sysinstall/help/tcp.hlp b/usr.sbin/sysinstall/help/tcp.hlp
new file mode 100644
index 0000000..d79cf27
--- /dev/null
+++ b/usr.sbin/sysinstall/help/tcp.hlp
@@ -0,0 +1,34 @@
+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 :-):
+
+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. The following strings are recognized:
+
+ "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.
+
+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..d0819fe
--- /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 chose
+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
+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 setup 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 where 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 with 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..19c8264
--- /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 */
+
+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..6d465ed
--- /dev/null
+++ b/usr.sbin/sysinstall/index.c
@@ -0,0 +1,824 @@
+/*
+ * 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 */
+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.",
+ "applications", "User application software.",
+ "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.",
+ "documentation", "Document preparation utilities.",
+ "editors", "Common text editors.",
+ "elisp", "Things related to Emacs Lisp.",
+ "emulators", "Utilities for emulating other OS types.",
+ "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.",
+ "gnome", "Components of the Gnome Desktop environment.",
+ "graphics", "Graphics libraries and utilities.",
+ "haskell", "Software related to the Haskell language.",
+ "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.",
+ "korean", "Ported software for the Korean market.",
+ "lang", "Computer languages.",
+ "languages", "Computer languages.",
+ "libraries", "Software development libraries.",
+ "linux", "Linux programs that can be run under binary compatibility.",
+ "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.",
+ "news", "USENET News support software.",
+ "numeric", "Mathematical computation software.",
+ "offix", "An office automation suite of sorts.",
+ "orphans", "Packages without a home elsewhere.",
+ "palm", "Software support for the Palm(tm) series.",
+ "parallel", "Applications dealing with parallelism in computing.",
+ "perl5", "Utilities/modules for the PERL5 language.",
+ "picobsd", "Ports to support PicoBSD.",
+ "pilot", "Software support for the 3Com Palm Pilot(tm) series.",
+ "plan9", "Software from the Plan9 operating system.",
+ "portuguese", "Ported software for the Portuguese market.",
+ "print", "Utilities for dealing with printing.",
+ "printing", "Utilities for dealing with printing.",
+ "programming", "Software development utilities and libraries.",
+ "python", "Software related to the Python language.",
+ "ruby", "Software related to the Ruby language.",
+ "russian", "Ported software for the Russian market.",
+ "science", "Scientific software.",
+ "security", "System security software.",
+ "shells", "Various shells (tcsh, bash, etc).",
+ "sysutils", "Various system utilities.",
+ "tcl75", "TCL v7.5 and packages that depend on it.",
+ "tcl76", "TCL v7.6 and packages that depend on it.",
+ "tcl80", "TCL v8.0 and packages that depend on it.",
+ "tcl81", "TCL v8.1 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.",
+ "textproc", "Text processing/search utilities.",
+ "tk41", "Tk4.1 and packages that depend on it.",
+ "tk42", "Tk4.2 and packages that depend on it.",
+ "tk80", "Tk8.0 and packages that depend on it.",
+ "tk81", "Tk8.1 and packages that depend on it.",
+ "tk82", "Tk8.2 and packages that depend on it.",
+ "tk83", "Tk8.3 and packages that depend on it.",
+ "tkstep80", "tkstep wm and packages that depend on it.",
+ "troff", "TROFF text formatting utilities.",
+ "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 (browers, HTTP servers, etc).",
+ "x11", "X Window System based utilities.",
+ "x11-clocks", "X Window System based clocks.",
+ "x11-fm", "X Window System based file managers.",
+ "x11-fonts", "X Window System fonts and font utilities.",
+ "x11-servers", "X Window System servers.",
+ "x11-toolkits", "X Window System based development toolkits.",
+ "x11-wm", "X Window System window managers.",
+ "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->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
+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.
+ */
+
+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, '|');
+ cp += copy_to_sep(pathto, cp, '|');
+ cp += copy_to_sep(prefix, cp, '|');
+ cp += copy_to_sep(comment, cp, '|');
+ cp += copy_to_sep(descr, cp, '|');
+ cp += copy_to_sep(maint, cp, '|');
+ cp += copy_to_sep(cats, cp, '|');
+ cp += copy_to_sep(junk, cp, '|'); /* build deps - not used */
+ cp += copy_to_sep(rdeps, cp, '|');
+ if (index(cp, '|'))
+ cp += copy_to_sep(junk, cp, '|'); /* url - not used */
+ else {
+ strncpy(junk, cp, 1023);
+ *volume = 0;
+ return 0;
+ }
+ 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) */
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+ int n, rval, maxname;
+ 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) {
+ int 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 status = DITEM_SUCCESS;
+ PkgNodePtr tmp2;
+ IndexEntryPtr id = who->data;
+ WINDOW *w = savescr();
+
+ /*
+ * 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)
+ return DITEM_SUCCESS;
+
+ /*
+ * What if the package is not available on the current media volume?
+ *
+ */
+ if (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);
+ /* XXX, at this point we check to see if this is the
+ * correct disc, and if not, we loop */
+ } else {
+ return DITEM_FAILURE;
+ }
+ }
+
+ 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);
+ if (DITEM_STATUS(status) != DITEM_SUCCESS) {
+ 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;
+ }
+ }
+ /* Done with the deps? Load the real m'coy */
+ if (DITEM_STATUS(status) == DITEM_SUCCESS) {
+ 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();
+
+ /* 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..e52d0e2
--- /dev/null
+++ b/usr.sbin/sysinstall/install.c
@@ -0,0 +1,1261 @@
+/*
+ * 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/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 <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;
+
+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, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **tdev, Chunk **hdev)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev, *tmpdev, *homedev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ if (rdev)
+ *rdev = NULL;
+ if (sdev)
+ *sdev = NULL;
+ if (udev)
+ *udev = NULL;
+ if (vdev)
+ *vdev = NULL;
+ if (tdev)
+ *tdev = NULL;
+ if (hdev)
+ *hdev = NULL;
+ rootdev = swapdev = usrdev = vardev = tmpdev = homedev = NULL;
+
+ /* 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
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
+ if (rootdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one root device set?!\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ rootdev = c2;
+ if (isDebug())
+ msgDebug("Found rootdev at %s!\n", rootdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
+ if (usrdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /usr filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ usrdev = c2;
+ if (isDebug())
+ msgDebug("Found usrdev at %s!\n", usrdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
+ if (vardev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /var filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ vardev = c2;
+ if (isDebug())
+ msgDebug("Found vardev at %s!\n", vardev->name);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/tmp")) {
+ if (tmpdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /tmp filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ tmpdev = c2;
+ if (isDebug())
+ msgDebug("Found tmpdev at %s!\n", tmpdev->name);
+ }
+ } else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/home")) {
+ if (homedev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /home filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ homedev = c2;
+ if (isDebug())
+ msgDebug("Found homedev at %s!\n", homedev->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 && !swapdev) {
+ swapdev = c2;
+ if (isDebug())
+ msgDebug("Found swapdev at %s!\n", swapdev->name);
+ break;
+ }
+#ifndef __ia64__
+ }
+ }
+#endif
+ }
+ }
+
+ /* Copy our values over */
+ if (rdev)
+ *rdev = rootdev;
+ if (sdev)
+ *sdev = swapdev;
+ if (udev)
+ *udev = usrdev;
+ if (vdev)
+ *vdev = vardev;
+ if (tdev)
+ *tdev = tmpdev;
+ if (hdev)
+ *hdev = homedev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && 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;
+ }
+ 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();
+ return DITEM_SUCCESS;
+ FixItMode = 0;
+}
+
+int
+installFixitCDROM(dialogMenuItem *self)
+{
+ struct stat sb;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+ (void)unlink("/mnt2");
+ (void)rmdir("/mnt2");
+
+ while (1) {
+ 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 (msgYesNo("Unable to mount the disc - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+
+ /* 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();
+ 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);
+ }
+
+ /* 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();
+ }
+ 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 (200MB 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 (200MB 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_LINUX
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to enable Linux binary compatibility?"))
+ (void)configLinux(self);
+#endif
+
+#ifdef __alpha__
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to enable OSF/1 binary compatibility?"))
+ (void)configOSF1(self);
+#endif
+
+#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();
+
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to configure your X server at this time?"))
+ (void)configXSetup(self);
+ }
+
+ 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 just the CRYPTO 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 (!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;
+ int kstat = 1;
+
+ /* 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 (!kstat || !OnVTY)
+ fprintf(fp, "# -- sysinstall generated deltas -- #\n");
+ if (!kstat)
+ fprintf(fp, "userconfig_script_load=\"YES\"\n");
+ if (!OnVTY)
+ 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 build for bin */
+ vsystem("newaliases");
+
+ /* 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");
+
+ /* Do all the last ugly work-arounds here */
+ }
+ 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, *rootdev, *swapdev;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ Boolean upgrade = FALSE;
+#if defined(__ia64__)
+ char efi_bootdir[FILENAME_MAX];
+#endif
+
+ /* 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, &rootdev, &swapdev, NULL, NULL, NULL, NULL))
+ return DITEM_FAILURE;
+
+ if (rootdev)
+ root = (PartInfo *)rootdev->private_data;
+ else
+ root = NULL;
+
+ command_clear();
+ if (swapdev && RunningAsInit) {
+ /* As the very first thing, try to get ourselves some swap space */
+ sprintf(dname, "/dev/%s", swapdev->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 (rootdev && RunningAsInit) {
+ /* Next, create and/or mount the root device */
+ sprintf(dname, "/dev/%s", rootdev->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", rootdev->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. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ 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", rootdev->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 == rootdev)
+ 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 == swapdev)
+ 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);
+
+ /*
+ * Create a directory boot on the EFI filesystem and create a
+ * link boot on the root filesystem pointing to the one on the
+ * EFI filesystem. That way, we install the loader, kernel
+ * and modules on the EFI filesystem.
+ */
+ sprintf(bootdir, "%s", RunningAsInit ? "/mnt" : "");
+ sprintf(efi_bootdir, "%s/%s", bootdir, pi->mountpoint);
+ strcat(bootdir, "/boot");
+ strcat(efi_bootdir, "/boot");
+ command_func_add(pi->mountpoint, Mkdir_command, efi_bootdir);
+
+ /* Make a relative link. */
+ p = &efi_bootdir[(RunningAsInit) ? 4 : 0];
+ while (*p == '/')
+ p++;
+ symlink(p, bootdir);
+ }
+#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)
+{
+ char *cp;
+
+ /* Set default startup options */
+ variable_set2(VAR_RELNAME, getRelname(), 0);
+ variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
+ variable_set2(VAR_TAPE_BLOCKSIZE, DEFAULT_TAPE_BLOCKSIZE, 0);
+ variable_set2(VAR_INSTALL_ROOT, "/", 0);
+ variable_set2(VAR_INSTALL_CFG, "install.cfg", 0);
+ variable_set2(VAR_SKIP_PCCARD, "NO", 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);
+ 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..fb7ad88
--- /dev/null
+++ b/usr.sbin/sysinstall/install.cfg
@@ -0,0 +1,99 @@
+# This is the 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=bin doc manpages info compat21 des src sbase ssys
+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
+################################
+
+################################
+
+# This assumes that slice 1 is a DOS partition and mounts it as /dos,
+# which is the case on my laptop.
+#
+# All sizes are expressed in 512 byte blocks!
+
+# A 20MB root partition
+ad0s1-1=ufs 40960 /
+# And a 20MB swap partition
+ad0s1-2=swap 40960 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.
+disk=ad1
+partition=exclusive
+diskPartitionEditor
+
+ad1s1-1=ufs 40960 /var
+ad1s1-2=ufs 0 /usr/src
+diskLabelEditor
+################################
+
+################################
+# And the 3rd.
+disk=da0
+partition=exclusive
+diskPartitionEditor
+
+da0s1-1=swap 40960 none
+da0s1-2=ufs 0 /tmp
+diskLabelEditor
+################################
+
+
+# OK, everything is set. Do it!
+installCommit
+
+# Install some packages at the end.
+package=bash-1.14.7
+packageAdd
+package=ncftp-2.4.2
+packageAdd
+package=tcsh-6.07.02
+packageAdd
diff --git a/usr.sbin/sysinstall/installUpgrade.c b/usr.sbin/sysinstall/installUpgrade.c
new file mode 100644
index 0000000..aa7ec88
--- /dev/null
+++ b/usr.sbin/sysinstall/installUpgrade.c
@@ -0,0 +1,498 @@
+/*
+ * 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, "dhclient.conf", TRUE, NULL },
+ { JUST_COPY, "disktab", TRUE, NULL },
+ { JUST_COPY, "dm.conf", 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, "make.conf", TRUE, NULL },
+ { JUST_COPY, "manpath.config", TRUE, NULL },
+ { JUST_COPY, "master.passwd", FALSE, 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, "pam.conf", TRUE, NULL },
+ { JUST_COPY, "passwd", TRUE, NULL },
+ { JUST_COPY, "periodic", TRUE, NULL },
+ { JUST_COPY, "ppp", TRUE, NULL },
+ { JUST_COPY, "printcap", TRUE, NULL },
+ { JUST_COPY, "profile", 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, "spwd.db", TRUE, NULL },
+ { JUST_COPY, "ssh", TRUE, NULL },
+ { JUST_COPY, "syslog.conf", TRUE, NULL },
+ { JUST_COPY, "ttys", TRUE, NULL },
+ { JUST_COPY, "uucp", TRUE, NULL },
+ { 0 },
+};
+
+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 unless 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 /usr/sbin /usr/bin /usr/lib /usr/libexec /kernel*");
+
+ if (file_readable("/kernel")) {
+ msgNotify("Moving old kernel to /kernel.prev");
+ if (system("mv /kernel /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 /kernel.prev should this upgrade\n"
+ "fail for any reason and you need to boot your old 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, 3)}' /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 befor 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;
+ }
+
+ if (file_readable("/kernel")) {
+ msgNotify("Moving old kernel to /kernel.prev");
+ if (!system("chflags noschg /kernel && mv /kernel /kernel.prev")) {
+ /* Give us a working kernel in case we crash and reboot */
+ system("cp /kernel.prev /kernel");
+ }
+ }
+
+ 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..3c53a00
--- /dev/null
+++ b/usr.sbin/sysinstall/keymap.c
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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..3f27e589
--- /dev/null
+++ b/usr.sbin/sysinstall/label.c
@@ -0,0 +1,1706 @@
+/*
+ * 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 <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 80
+#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 256
+#define USR_DEFAULT_SIZE 3072
+#define VAR_DEFAULT_SIZE 256
+#define TMP_DEFAULT_SIZE 256
+#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 192
+#define USR_NOMINAL_SIZE 512
+#define VAR_NOMINAL_SIZE 64
+#define TMP_NOMINAL_SIZE 64
+#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(char *mpoint, Boolean newfs)
+{
+ PartInfo *pi;
+
+ if (!mpoint)
+ mpoint = "/change_me";
+
+ pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
+
+ pi->do_newfs = newfs;
+
+ 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;
+}
+
+#if defined(__ia64__)
+static PartInfo *
+new_efi_part(char *mpoint, Boolean newfs)
+{
+ PartInfo *pi;
+
+ if (!mpoint)
+ mpoint = "/efi";
+
+ pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
+
+ pi->do_newfs = newfs;
+ pi->newfs_type = NEWFS_MSDOS;
+
+ return pi;
+}
+#endif
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+ Boolean newfs;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "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(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)
+{
+ extern void print_label_chunks();
+ 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(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].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].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(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);
+ }
+#if defined(__ia64__)
+ else if (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_efi_part(pi->mountpoint, !pi->do_newfs);
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+#endif
+ 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"
+ "sysinstall first.");
+ }
+ 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.
+ *
+ * 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;
+ struct chunk *root_chunk = NULL;
+ struct chunk *swap_chunk = NULL;
+ struct chunk *usr_chunk = NULL;
+ struct chunk *var_chunk = NULL;
+ struct chunk *tmp_chunk = NULL;
+ struct chunk *home_chunk = NULL;
+ int mib[2];
+ unsigned long physmem;
+ size_t size;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+ Chunk *tmpdev, *homedev;
+ 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, &rootdev, &swapdev, &usrdev,
+ &vardev, &tmpdev, &homedev);
+ if (!rootdev) {
+ sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
+
+ root_chunk = 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 (!root_chunk) {
+ *req = 1;
+ msg = "Unable to create the root partition. Too big?";
+ goto done;
+ }
+ root_chunk->private_data = new_part("/", TRUE);
+ root_chunk->private_free = safe_free;
+ root_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!swapdev) {
+ 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;
+ }
+ swap_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_SWAP, CHUNK_AUTO_SIZE);
+ if (!swap_chunk) {
+ *req = 1;
+ msg = "Unable to create the swap partition. Too big?";
+ goto done;
+ }
+ swap_chunk->private_data = 0;
+ swap_chunk->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ if (!vardev) {
+ sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, VAR_DEFAULT_SIZE, perc);
+
+ var_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!var_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /var - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ var_chunk->private_data = new_part("/var", TRUE);
+ var_chunk->private_free = safe_free;
+ var_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!tmpdev && !variable_get(VAR_NO_TMP)) {
+ sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
+
+ tmp_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!tmp_chunk) {
+ *req = 1;
+ msg = "Not enough free space for /tmp - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ tmp_chunk->private_data = new_part("/tmp", TRUE);
+ tmp_chunk->private_free = safe_free;
+ tmp_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (!usrdev && !variable_get(VAR_NO_USR)) {
+ sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
+#if AUTO_HOME == 0
+ 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!";
+ }
+
+ usr_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!usr_chunk) {
+ 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;
+ }
+ usr_chunk->private_data = new_part("/usr", TRUE);
+ usr_chunk->private_free = safe_free;
+ usr_chunk->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#if AUTO_HOME == 1
+ if (!homedev && !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;
+ }
+
+ home_chunk = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!home_chunk) {
+ 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;
+ }
+ home_chunk->private_data = new_part("/home", TRUE);
+ home_chunk->private_free = safe_free;
+ home_chunk->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 (root_chunk)
+ Delete_Chunk(root_chunk->disk, root_chunk);
+ if (swap_chunk)
+ Delete_Chunk(swap_chunk->disk, swap_chunk);
+ if (var_chunk)
+ Delete_Chunk(var_chunk->disk, var_chunk);
+ if (tmp_chunk)
+ Delete_Chunk(tmp_chunk->disk, tmp_chunk);
+ if (usr_chunk)
+ Delete_Chunk(usr_chunk->disk, usr_chunk);
+ if (home_chunk)
+ Delete_Chunk(home_chunk->disk, home_chunk);
+ 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(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(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..695754b
--- /dev/null
+++ b/usr.sbin/sysinstall/main.c
@@ -0,0 +1,187 @@
+/*
+ * 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>
+
+const char *StartName; /* Initial contents of argv[0] */
+
+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;
+
+ /* 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;
+ }
+
+#ifdef PC98
+ {
+ /* XXX */
+ char *p = getenv("TERM");
+ if (p && strcmp(p, "cons25") == 0)
+ putenv("TERM=cons25w");
+ }
+#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");
+ }
+
+ /* Initialize PC Card, if we haven't already done so. */
+#ifdef PCCARD_ARCH
+ if (!variable_cmp(VAR_SKIP_PCCARD, "YES") &&
+ variable_get(VAR_SKIP_PCCARD)!=1 &&
+ !pvariable_get("pccardInitialize")) {
+ pccardInitialize();
+ pvariable_set("pccardInitialize=1");
+ }
+#endif
+
+ /* Initialize USB, if we haven't already done so. */
+ if (!pvariable_get("usbInitialize")) {
+ usbInitialize();
+ pvariable_set("usbInitialize=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);
+ }
+
+ /* 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(__alpha__) || 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\n"
+ "(be sure to remove any floppies/CDs/DVDs from the drives).")
+#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..cbdc01c
--- /dev/null
+++ b/usr.sbin/sysinstall/media.c
@@ -0,0 +1,882 @@
+/*
+ * 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 "";
+}
+
+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);
+}
+
+static int
+tapeHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_TAPE);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a tape drive.
+ */
+int
+mediaSetTape(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_TAPE);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No tape drive 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(&MenuMediaTape, DEVICE_TYPE_TAPE, tapeHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create tape drive menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ if (mediaDevice) {
+ char *val;
+
+ val = msgGetInput("/var/tmp", "Please enter the name of a temporary directory containing\n"
+ "sufficient space for holding the contents of this tape (or\n"
+ "tapes). The contents of this directory will be removed\n"
+ "after installation, so be sure to specify a directory that\n"
+ "can be erased afterwards!\n");
+ if (!val)
+ mediaDevice = NULL;
+ else
+ mediaDevice->private = strdup(val);
+ }
+ 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, 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];
+ int 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(), "--block-size", mediaTapeBlocksize(), (char *)0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (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",
+ USE_GZIP ? "gunzip" : "bunzip2", 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(), "--block-size", mediaTapeBlocksize(), (char *)0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (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",
+ USE_GZIP ? "gunzip" : "bunzip2", 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..6830754
--- /dev/null
+++ b/usr.sbin/sysinstall/menus.c
@@ -0,0 +1,2283 @@
+/*
+ * 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;
+ CRYPTODists |= (DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ CRYPTODists &= ~(DIST_CRYPTO_SCRYPTO | DIST_CRYPTO_SSECURE |
+ DIST_CRYPTO_SKERBEROS5);
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Misc(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_MISC_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Misc(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_MISC_ALL;
+ if (!XF86ServerDists && !XF86FontDists)
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Servers(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_SERVER;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Servers(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_SERVER;
+ XF86ServerDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_FONTS;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_FONTS;
+ XF86FontDists = 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
+checkDistXDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_ALL);
+}
+
+static int
+checkDistKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistXKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, DIST_XF86) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, 0);
+}
+
+static int
+checkDistXUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, DIST_XF86);
+}
+
+static int
+checkDistMinimum(dialogMenuItem *self)
+{
+ return Dists == (DIST_BASE | DIST_CRYPTO);
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+ return Dists == DIST_ALL && CRYPTODists == DIST_CRYPTO_ALL &&
+ _IS_SET(SrcDists, DIST_SRC_ALL) &&
+ _IS_SET(XF86Dists, DIST_XF86_ALL) &&
+ _IS_SET(XF86ServerDists, DIST_XF86_SERVER_ALL) &&
+ _IS_SET(XF86FontDists, DIST_XF86_FONTS_ALL);
+}
+
+static int
+srcFlagCheck(dialogMenuItem *item)
+{
+ return SrcDists;
+}
+
+static int
+x11FlagCheck(dialogMenuItem *item)
+{
+ return Dists & DIST_XF86;
+}
+
+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 },
+#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", "Load default settings.", NULL, dispatch_load_floppy },
+#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, X Developer", "Select X developer's distribution.", checkDistXDeveloper, distSetXDeveloper },
+ { " Dists, Kern Developer", "Select kernel developer's distribution.", checkDistKernDeveloper, distSetKernDeveloper },
+ { " Dists, User", "Select average user distribution.", checkDistUser, distSetUser },
+ { " Dists, X User", "Select average X user distribution.", checkDistXUser, distSetXUser },
+ { " Distributions, Adding", "Installing additional distribution sets", NULL, distExtractAll },
+ { " Distributions, XFree86","XFree86 distribution menu.", NULL, distSetXF86 },
+ { " Documentation", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { " Doc, README", "The distribution README file.", NULL, dmenuDisplayFile, NULL, "README" },
+ { " Doc, Early Adopter's", "Early Adopter's Guide to FreeBSD 5.0.", NULL, dmenuDisplayFile, NULL, "EARLY" },
+ { " Doc, Errata", "The distribution errata.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { " Doc, Hardware", "The distribution hardware guide.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { " Doc, Install", "The distribution installation guide.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { " 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, Tape", "Select tape installation media.", NULL, mediaSetTape },
+ { " 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, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { " 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 },
+ { " XFree86, Fonts", "XFree86 Font selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { " XFree86, Server", "XFree86 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { NULL } },
+};
+
+/* 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.",
+ "Press F1 for Installation Guide", /* help line */
+ "INSTALL", /* help file */
+ { { " 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, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+#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_floppy },
+ { "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 Early Adopter's", "Early Adopter's Guide to FreeBSD 5.0.", NULL, dmenuDisplayFile, NULL, "EARLY" },
+ { "3 Errata", "Late-breaking, post-release news.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { "4 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { "5 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { "6 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "7 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { "8 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "9 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/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { 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/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { "4 COM3", "Serial mouse on COM3 (/dev/cuaa2)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa2" },
+ { "5 COM4", "Serial mouse on COM4 (/dev/cuaa3)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa3" },
+ { "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.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { 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.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { 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!",
+ "INSTALL",
+ { { "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", "snapshots.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://snapshots.jp.freebsd.org" },
+
+ { "IPv6 Ireland", "ftp3.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ie.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" },
+
+ { "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" },
+
+ { "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" },
+
+ { "Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hk.super.net" },
+
+ { "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" },
+
+ { "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" },
+
+ { "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 MenuMediaTape = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a tape drive type",
+ "FreeBSD can be installed from tape drive, though this installation\n"
+ "method requires a certain amount of temporary storage in addition\n"
+ "to the space required by the distribution itself (tape drives make\n"
+ "poor random-access devices, so we extract _everything_ on the tape\n"
+ "in one pass). If you have sufficient space for this, then you should\n"
+ "select one of the following tape devices detected on your system.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { 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 } },
+};
+
+/* 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 },
+ { "9 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "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, binaries and X Window System",
+ 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 X-Developer", "Same as above + X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "6 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "7 X-Kern-Developer", "Same as above + X Window System",
+ checkDistXKernDeveloper, distSetXKernDeveloper },
+ { "8 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "9 X-User", "Same as above + X Window System",
+ checkDistXUser, distSetXUser },
+ { "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, binaries and X Window System",
+ 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 },
+#ifdef __i386__
+ { " compat1x", "FreeBSD 1.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT1X },
+ { " compat20", "FreeBSD 2.0 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT20 },
+ { " compat21", "FreeBSD 2.1 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT21 },
+ { " compat22", "FreeBSD 2.2.x and 3.0 a.out binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT22 },
+ { " compat3x", "FreeBSD 3.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT3X },
+#endif
+#if __FreeBSD__ >= 4 && (defined(__i386__) || defined(__alpha__))
+ { " compat4x", "FreeBSD 4.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT4X },
+#endif
+ { " crypto", "Basic encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_CRYPTO, },
+ { " 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 },
+ { " 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},
+ { " perl", "The Perl distribution",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PERL },
+ { " XFree86", "The XFree86 distribution",
+ x11FlagCheck, distSetXF86 },
+ { 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 },
+ { " contrib", "/usr/src/contrib (contributed software)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CONTRIB },
+ { " 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 },
+ { " 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 },
+ { " 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 },
+ { " scrypto", "/usr/src/crypto (contrib encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SCRYPTO },
+ { " share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { " skrb5", "/usr/src/kerberos5 (sources for Kerberos5)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SKERBEROS5 },
+ { " ssecure", "/usr/src/secure (BSD encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CRYPTODists, '[', 'X', ']', DIST_CRYPTO_SSECURE },
+ { " 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 MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first option, xf86cfg, is fully graphical.\n"
+ "The second option provides a menu-based interface similar to\n"
+ "what you are currently using. "
+ "The third option, xf86config, is\n"
+ "a more simplistic shell-script based tool and less friendly to\n"
+ "new users, but it may work in situations where the other options\n"
+ "do not.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 xf86cfg", "Fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg" },
+ { "3 xf86cfg -textmode", "ncurses-based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86cfg -textmode" },
+ { "4 xf86config", "Shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { "D XDesktop", "X already set up, just do desktop configuration.",
+ NULL, dmenuSubmenu, NULL, &MenuXDesktops },
+ { NULL } },
+};
+
+DMenu MenuXDesktops = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the default X desktop to use.",
+ "By default, XFree86 comes with a fairly vanilla desktop which\n"
+ "is based around the twm(1) window manager and does not offer\n"
+ "much in the way of features. It does have the advantage of\n"
+ "being a standard part of X so you don't need to load anything\n"
+ "extra in order to use it. If, however, you have access to a\n"
+ "reasonably full packages collection on your installation media,\n"
+ "you can choose any one of the following desktops as alternatives.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { "2 KDE", "The K Desktop Environment (Lite Edition)",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=kde" },
+ { "3 GNOME 2", "The GNOME 2 Desktop Environment (Lite Edition)",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=gnome2" },
+ { "4 Afterstep", "The Afterstep window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=afterstep" },
+ { "5 Windowmaker", "The Windowmaker window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=windowmaker" },
+ { "6 fvwm", "The fvwm window manager",
+ NULL, dmenuSetVariable, NULL, VAR_DESKSTYLE "=fvwm2" },
+ { NULL } },
+};
+
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 Distribution",
+ "Please select the components you need from the XFree86\n"
+ "distribution sets.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "Basic", "Basic component menu (required)", NULL, dmenuSubmenu, NULL, &MenuXF86SelectCore },
+ { "Server", "X server menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "Fonts", "Font set menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 base distribution types",
+ "Please check off the basic XFree86 components you wish to install.\n"
+ "Bin, lib, and set are recommended for a minimum installaion.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all below",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all below",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { " bin", "Client applications",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CLIENTS },
+ { " lib", "Shared libraries and data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+ { " man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { " doc", "Documentation",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_DOC },
+ { " prog", "Programming tools",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectFonts = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Font distribution selection.",
+ "Please check off the individual font distributions you wish to\n\
+install. At the minimum, you should install the standard\n\
+75 DPI and misc fonts if you're also installing a server\n\
+(these are selected by default).",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { " fnts", "Standard miscellaneous fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_BITMAPS },
+ { " f75", "75 DPI fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_75 },
+ { " f100", "100 DPI fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_100 },
+ { " fcyr", "Cyrillic Fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_CYR },
+ { " fscl", "Speedo and Type scalable fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SCALE },
+ { " server", "Font server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SERVER },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectServer = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "X Server selection.",
+ "Please check off the types of X servers you wish to install.\n",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { " srv", "Standard Graphics Framebuffer",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_FB },
+ { " nest", "Nested X Server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_NEST },
+ { " prt", "X Print Server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_PRINT },
+ { " vfb", "Virtual Framebuffer",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VFB },
+ { 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.",
+ "Press F1 to read the installation guide",
+ "INSTALL",
+ { { "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 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 },
+ { "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 } },
+};
+#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 },
+ { " XFree86", "Configure XFree86 Server",
+ NULL, configXSetup },
+ { " Desktop", "Configure XFree86 Desktop",
+ NULL, configXDesktop },
+ { " 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
+#ifdef PCCARD_ARCH
+ { " pccard", "Enable PCCARD (AKA PCMCIA) services (also laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "pccard_enable=YES" },
+ { " pccard mem", "Set PCCARD memory address (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_mem" },
+ { " pccard ifconfig", "List of PCCARD ethernet devices to configure",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_ifconfig" },
+#endif
+ { " usbd", "Enable USB daemon (detect USB attach / detach)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "usbd_enable=YES" },
+ { " usbd flags", "Set default flags to usbd (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "usbd_flags" },
+ { " ", " -- ", 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 WITH_LINUX
+ { " Linux", "This host wants to be able to run Linux binaries.",
+ dmenuVarCheck, configLinux, NULL, VAR_LINUX_ENABLE "=YES" },
+#endif
+#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
+#ifdef __alpha__
+ { " OSF/1", "This host wants to be able to run DEC OSF/1 binaries.",
+ dmenuVarCheck, configOSF1, NULL, VAR_OSF1_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 },
+ { "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", "augean.eleceng.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=augean.eleceng.adelaide.edu.au" },
+ { "Australia #2", "ntp.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.adelaide.edu.au" },
+ { "Australia #3", "ntp.saard.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.saard.net" },
+ { "Australia #4", "time.deakin.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.deakin.edu.au" },
+ { "Australia #5", "time.esec.com.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.esec.com.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", "ntp.cais.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cais.rnp.br" },
+ { "Brazil #2", "ntp.pop-df.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.pop-df.rnp.br" },
+ { "Brazil #3", "ntp.ufes.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ufes.br" },
+ { "Brazil #4", "ntp1.pucpr.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.pucpr.br" },
+ { "Canada", "ntp.cpsc.ucalgary.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cpsc.ucalgary.ca" },
+ { "Canada #2", "ntp1.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cmc.ec.gc.ca" },
+ { "Canada #3", "ntp2.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.cmc.ec.gc.ca" },
+ { "Canada #4", "tick.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.utoronto.ca" },
+ { "Canada #5", "time.chu.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.chu.nrc.ca" },
+ { "Canada #6", "time.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nrc.ca" },
+ { "Canada #7", "timelord.uregina.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timelord.uregina.ca" },
+ { "Canada #8", "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" },
+ { "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.incaf.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.incaf.net" },
+ { "Ireland", "ntp.maths.tcd.ie",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.maths.tcd.ie" },
+ { "Italy", "ntps.net4u.it",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=" },
+ { "Japan", "ntp.cyber-fleet.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cyber-fleet.net" },
+ { "Korea", "time.nuri.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nuri.net" },
+ { "Mexico", "ntp2a.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.audiotel.com.mx" },
+ { "Mexico #2", "ntp2b.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.audiotel.com.mx" },
+ { "Mexico #3", "ntp2c.audiotel.com.mx",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.audiotel.com.mx" },
+ { "Nigeria", "ntp.supernet300.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.supernet300.com" },
+ { "Netherlands", "ntp1.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.theinternetone.net" },
+ { "Netherlands #2", "ntp2.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.theinternetone.net" },
+ { "Netherlands #3", "ntp3.theinternetone.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp3.theinternetone.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" },
+ { "Portugal", "bug.fe.up.pt",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bug.fe.up.pt" },
+ { "Romania", "ntp.ip.ro",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ip.ro" },
+ { "Russia", "ntp.psn.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.psn.ru" },
+ { "Russia #2", "sign.chg.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sign.chg.ru" },
+ { "Sweden", "ntp.lth.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.lth.se" },
+ { "Singapore", "ntp.shim.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.shim.org" },
+ { "Slovenia", "calvus.rzs-hm.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=calvus.rzs-hm.si" },
+ { "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" },
+ { "United Kingdom", "ntp.exnet.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.exnet.com" },
+ { "United Kingdom #2", "ntp0.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.uk.uu.net" },
+ { "United Kingdom #3", "ntp1.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.uk.uu.net" },
+ { "United Kingdom #4", "ntp2.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.uk.uu.net" },
+ { "United Kingdom #5", "ntp2a.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.mcc.ac.uk" },
+ { "United Kingdom #6", "ntp2b.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.mcc.ac.uk" },
+ { "United Kingdom #7", "ntp2c.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.mcc.ac.uk" },
+ { "United Kingdom #8", "ntp2d.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2d.mcc.ac.uk" },
+ { "United Kingdom #9", "tick.tanac.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.tanac.net" },
+ { "U.S. AR", "sushi.compsci.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", "clock.linuxshell.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.linuxshell.net" },
+ { "U.S. NY #2", "ntp.ctr.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ctr.columbia.edu" },
+ { "U.S. NY #3", "ntp0.cornell.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.cornell.edu" },
+ { "U.S. NY #4", "ntp1.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.mpis.net" },
+ { "U.S. NY #5", "ntp2.mpis.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.mpis.net" },
+ { "U.S. NY #6", "sundial.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sundial.columbia.edu" },
+ { "U.S. NY #7", "timex.cs.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.cs.columbia.edu" },
+ { "U.S. OK", "constellation.ecn.uoknor.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=constellation.ecn.uoknor.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.cox.smu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cox.smu.edu" },
+ { "U.S. TX #2", "ntp.fnbhs.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.fnbhs.com" },
+ { "U.S. TX #3", "ntp.tmc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tmc.edu" },
+ { "U.S. TX #4", "ntp5.tamu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp5.tamu.edu" },
+ { "U.S. TX #5", "tick.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.greyware.com" },
+ { "U.S. TX #6", "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", "clock.tricity.wsu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tricity.wsu.edu" },
+ { "U.S. WA #2", "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" },
+ { "Venezuela", "ntp.linux.org.ve",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.linux.org.ve" },
+ { "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" },
+ { " 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" },
+ { "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 phosphor 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 used 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 firwalling 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..07cbc81
--- /dev/null
+++ b/usr.sbin/sysinstall/misc.c
@@ -0,0 +1,529 @@
+/*
+ * 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>
+
+/* 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);
+}
+
diff --git a/usr.sbin/sysinstall/modules.c b/usr.sbin/sysinstall/modules.c
new file mode 100644
index 0000000..859994a
--- /dev/null
+++ b/usr.sbin/sysinstall/modules.c
@@ -0,0 +1,229 @@
+/*-
+ * 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) {
+ 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;
+ }
+
+ 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);
+
+ mediaClose();
+
+ deviceRescan();
+
+ errout:
+ 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..c57ad3d
--- /dev/null
+++ b/usr.sbin/sysinstall/network.c
@@ -0,0 +1,355 @@
+/*
+ * 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 (strcmp(cp, "DHCP")) {
+ msgDebug("ifconfig %s %s\n", dev->name, cp);
+ 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 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") && mknod("/dev/tun0", 0600 | S_IFCHR, makedev(52, 0))) {
+ 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..8e3a7d9
--- /dev/null
+++ b/usr.sbin/sysinstall/options.c
@@ -0,0 +1,341 @@
+/*
+ * 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(char *str)
+{
+ 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_TAPE:
+ return "Tape";
+
+ 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 TAPE_PROMPT "Please enter the tape block size in 512 byte blocks:"
+#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 },
+{ "Skip PCCARD", "Skip PC Card probing, do not use PC Card devices for installation",
+ OPT_IS_VAR, NULL, VAR_SKIP_PCCARD, 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 },
+{ "Tape Blocksize", "Tape media block size in 512 byte blocks",
+ OPT_IS_VAR, TAPE_PROMPT, VAR_TAPE_BLOCKSIZE, 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 },
+{ "Use Defaults", "Reset all values to startup defaults",
+ OPT_IS_FUNC, installVarDefaults, 0, resetLogo },
+{ 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..7f23fc3
--- /dev/null
+++ b/usr.sbin/sysinstall/package.c
@@ -0,0 +1,263 @@
+/*
+ * 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;
+
+ 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)
+ return index_extract(mediaDevice, &Top, tmp, FALSE);
+ 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 ext, last_msg, pathend, ret;
+ 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("PACKAGE_BUILDING", "t", 1);
+ 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/pccard.c b/usr.sbin/sysinstall/pccard.c
new file mode 100644
index 0000000..0ddfad8
--- /dev/null
+++ b/usr.sbin/sysinstall/pccard.c
@@ -0,0 +1,290 @@
+/*
+ * PC Card support for sysinstall
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1997-1999
+ * Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>. All rights reserved.
+ *
+ * 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
+ * use.
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/time.h>
+#include <pccard/cardinfo.h>
+
+int pccard_mode = 0;
+
+/*
+ * Set up defines for pccardd interrupt selection.
+ */
+#ifdef PC98
+#define IRQ_COUNT 11
+#else
+#define IRQ_COUNT 9
+#endif /* PC98 */
+#define IRQ_10 0x00001
+#define IRQ_11 0x00002
+#define IRQ_03 0x00004
+#define IRQ_09 0x00008
+#define IRQ_04 0x00010
+#define IRQ_07 0x00020
+#define IRQ_05 0x00040
+#define IRQ_06 0x00080
+#define IRQ_15 0x00100
+#ifdef PC98
+#define IRQ_12 0x00200
+#define IRQ_13 0x00400
+#endif /* PC98 */
+
+unsigned int CardIrq;
+
+typedef struct _irq {
+ char *my_name;
+ char *my_flag;
+ unsigned int my_mask;
+ unsigned int my_bit;
+} Irq;
+
+/* Fill in with potential free IRQs for pccardd */
+static Irq IrqTable[] = {
+ { "irq_03", "-i 3", ~IRQ_03, IRQ_03 },
+ { "irq_04", "-i 4", ~IRQ_04, IRQ_04 },
+ { "irq_05", "-i 5", ~IRQ_05, IRQ_05 },
+ { "irq_06", "-i 6", ~IRQ_06, IRQ_06 },
+ { "irq_07", "-i 7", ~IRQ_07, IRQ_07 },
+ { "irq_09", "-i 9", ~IRQ_09, IRQ_09 },
+ { "irq_10", "-i 10", ~IRQ_10, IRQ_10 },
+ { "irq_11", "-i 11", ~IRQ_11, IRQ_11 },
+ { "irq_15", "-i 15", ~IRQ_15, IRQ_15 },
+#ifdef PC98
+ { "irq_12", "-i 12", ~IRQ_12, IRQ_12 },
+ { "irq_13", "-i 13", ~IRQ_13, IRQ_13 },
+#endif /* PC98 */
+ {NULL},
+};
+
+int
+pccardIrqReset(dialogMenuItem *self)
+{
+ CardIrq = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+checkTrue(dialogMenuItem *item)
+{
+ return TRUE;
+}
+
+DMenu MenuPCICMem = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select free address area used by PC Card controller",
+ "PC Card controller uses memory area to get card information.\n"
+ "Please specify an address that is not used by other devices.\n"
+ "If you're uncertain of detailed specification of your hardware,\n"
+#ifdef PC98
+ "leave it untouched (default == 0xd0000).\n"
+ "If you use PC-9801 P, NS/A, NX/C, NL/R or PC-9821 Ne please \n"
+ "select [DA] here.",
+#else
+ "leave it untouched (default == 0xd0000).",
+#endif /* PC98 */
+ "Press F1 for more HELP",
+ "pccard",
+ { { "Default", "I/O address 0xd0000 - 0xd3fff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=0"},
+ { "D4", "I/O address 0xd4000 - 0xd7fff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=1"},
+ { "D8", "I/O address 0xd8000 - 0xdbfff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=2"},
+ { "DC", "I/O address 0xdc000 - 0xdffff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=3"},
+#ifdef PC98
+ { "DA", "I/O address 0xda000 - 0xdbfff",
+ NULL, dmenuSetVariable, NULL, "_pcicmem=4"},
+#endif /* PC98 */
+ { NULL } },
+};
+
+DMenu MenuCardIRQ = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Please specify the IRQs that may be used by PC Cards",
+ "(NOTE: remove any cards that will NOT be used for installation).\n"
+ "The IRQs that you choose must be free (unshared), or you risk \n"
+ "subpar performance and/or a complete system lockup (choose wisely).\n"
+ "One way to determine which IRQs are available is to \"cheat\" and\n"
+ "use the Windows 9x/2000 Device Manager as a reference prior to the\n"
+ "installation.\n",
+ "Select Free IRQ for pccardd",
+ NULL,
+ { { "X Exit", "Exit this menu",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "Reset", "Reset selected IRQ list",
+ NULL, pccardIrqReset, NULL, NULL, ' ', ' ', ' ' },
+#ifdef PC98
+ { "3 IRQ 3", "(INT 0) is 2nd serial port, internal modem",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_03 },
+ { "4 IRQ 5", "(INT 1) is Infrared communication, SCSI I/F, (2nd serial)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_05 },
+ { "5 IRQ 6", "(INT 2) is PC Card Controller",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_06 },
+ { "6 IRQ 9", "(INT 3) is IDE disk Controller",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_09 },
+ { "7 IRQ 10", "(INT 41) is often free",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_10 },
+ { "8 IRQ 12", "(INT 5) is Internal sound",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_12 },
+ { "9 IRQ 13", "(INT 6) is Bus Mouse",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_13 },
+#else
+ { "3 IRQ 10", "IRQ 10 is often free (verify in BIOS)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_10 },
+ { "4 IRQ 11", "Verify IRQ 11 is not being used as PCI shared interrupt",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_11 },
+ { "5 IRQ 3", "IRQ 3 is often free in most laptops",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_03 },
+ { "6 IRQ 9", "IRQ 9 may be used by video or sound or USB",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_09 },
+ { "7 IRQ 4", "IRQ 4, usually COM1 (disable in BIOS to make free)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_04 },
+ { "8 IRQ 7", "IRQ 7, usually LPT1 (disable in BIOS to make free)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_07 },
+ { "9 IRQ 5", "IRQ 5, usually ISA Audio (disable in BIOS to make free)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_05 },
+ { "10 IRQ 15", "IRQ 15, usually Secondary IDE channel",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_15 },
+ { "11 IRQ 6", "IRQ 6 will be free if laptop only has USB floppy drive",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &CardIrq, '[', 'X', ']', IRQ_06 },
+#endif /* PC98 */
+ { NULL } },
+};
+
+void
+pccardInitialize(void)
+{
+ int fd;
+ int t;
+ int i;
+ int pcic_mem = 0xd0000;
+ int beep_newstat;
+ char card_device[16];
+ char card_irq[256] = "";
+ char temp[256];
+ char *spcic_mem;
+ char pccardd_cmd[256];
+ WINDOW *w;
+
+ pccard_mode = 1;
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ sprintf(card_device, CARD_DEVICE, 0);
+
+ if ((fd = open(card_device, O_RDWR)) < 0) {
+ msgDebug("Can't open PC Card controller %s.\n", card_device);
+ return;
+ }
+ else if (msgYesNo("Found PC Card slot(s).\n"
+ "Use PC Card device as installation media?\n")) {
+ return;
+ }
+ close(fd);
+
+ variable_set2("_pccard_install", "YES", 0);
+
+ dmenuOpenSimple(&MenuPCICMem, FALSE);
+ spcic_mem = variable_get("_pcicmem");
+ dmenuOpenSimple(&MenuCardIRQ, FALSE);
+
+ sscanf(spcic_mem, "%d", &t);
+ switch (t) {
+ case 0:
+ pcic_mem = 0xd0000;
+ variable_set2("pccard_mem", "DEFAULT", 1);
+ break;
+ case 1:
+ pcic_mem = 0xd4000;
+ variable_set2("pccard_mem", "0xd4000", 1);
+ break;
+ case 2:
+ pcic_mem = 0xd8000;
+ variable_set2("pccard_mem", "0xd8000", 1);
+ break;
+ case 3:
+ pcic_mem = 0xdc000;
+ variable_set2("pccard_mem", "0xdc000", 1);
+ break;
+#ifdef PC98
+ case 4:
+ pcic_mem = 0xda000;
+ variable_set2("pccard_mem", "0xda000", 1);
+ break;
+#endif /* PC98 */
+ }
+
+ /* get card_irq out of CardIrq somehow */
+ if (CardIrq) {
+ for (i = 0; i < IRQ_COUNT; i++) {
+ if ((CardIrq & IrqTable[i].my_bit) != 0) {
+ sprintf(temp, "%s %s", card_irq, IrqTable[i].my_flag);
+ strcpy(card_irq, temp);
+ }
+ }
+ }
+
+ w = savescr();
+ dialog_clear_norefresh();
+ msgConfirm("Now we start initializing PC Card controller and cards.\n"
+ "If you've executed this installer from a PC Card floppy\n"
+ "drive, this is the last chance to replace it with\n"
+ "installation media (PC Card Ethernet, CD, DVD, etc.).\n"
+ "Please insert installation media and press [Enter].\n"
+ "If you've not plugged the PC Card installation media\n"
+ "in yet, please plug it in now and press [Enter].\n"
+ "Otherwise, just press [Enter] to proceed.");
+
+ dialog_clear();
+ msgNotify("Initializing PC Card controller....");
+
+ if (!Fake) {
+ if ((fd = open(card_device, O_RDWR)) < 1) {
+ msgNotify("Can't open PC Card controller %s.\n", card_device);
+ restorescr(w);
+ return;
+ }
+
+ if (ioctl(fd, PIOCRWMEM, &pcic_mem) < 0) {
+ msgNotify("ioctl %s failed.\n", card_device);
+ restorescr(w);
+ return;
+ }
+ beep_newstat = 2;
+ if (ioctl(fd, PIOCSBEEP, &beep_newstat) < 0) {
+ msgNotify("Warning: unable to set pccard insertion beep type for %s",
+ card_device);
+ restorescr(w);
+ return;
+ }
+
+ }
+
+ strcpy(pccardd_cmd, "/stand/pccardd ");
+ strcat(pccardd_cmd, card_irq);
+ strcat(pccardd_cmd, " -z");
+
+ variable_set2("pccardd_flags", card_irq, 1);
+ variable_set2("pccard_enable", "YES", 1);
+
+ vsystem("%s", pccardd_cmd);
+ restorescr(w);
+}
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..b1b04bb
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.8
@@ -0,0 +1,1010 @@
+.\" 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 9, 1997
+.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's 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 configXSetup
+Configure the X display subsystem.
+.Pp
+.Sy Variables :
+None
+.It configXDesktop
+Configure the X desktop.
+.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. 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've 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 500MB 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 40960 /"
+A 20MB root file system (all sizes are in 512 byte blocks).
+.It Li "da0s2-2=swap 131072 /"
+A 64MB swap partition.
+.It Li "da0s2-3=ufs 204800 /var"
+A 100MB /var file system.
+.It Li "da0s2-4=ufs 0 /usr 1"
+With the balance of free space (around 316MB) 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 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 crypto
+Encryption binaries and libraries.
+.It Li compat1x
+Compatibility with
+.Fx
+1.x
+.It Li compat20
+Compatibility with
+.Fx 2.0
+.It Li compat21
+Compatibility with
+.Fx 2.1
+.It Li compat22
+.Fx 2.2
+and
+.Fx 3.0
+a.out binary compatibility
+.It Li compat3x
+Compatibility with
+.Fx
+3.x
+(available for
+.Fx 4.0
+systems only)
+.It Li compat4x
+Compatibility with
+.Fx
+4.x
+(available for
+.Fx 5.0
+systems only)
+.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 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 sbin
+/usr/src/bin
+.It Li ssbin
+/usr/src/sbin
+.It Li sshare
+/usr/src/share
+.It Li ssys
+/usr/src/sys
+.It Li subin
+/usr/src/usr.bin
+.It Li susbin
+/usr/src/usr.sbin
+.It Li ssmailcf
+/usr/src/usr.sbin/sendmail/cf
+.It Li XF86-xc
+XFree86 official sources.
+.It Li XF86-co
+XFree86 contributed sources.
+.It Li Xbin
+XFree86 binaries.
+.It Li Xcfg
+XFree86 configuration files.
+.It Li Xdoc
+XFree86 documentation.
+.It Li Xhtml
+XFree86 HTML documentation.
+.It Li Xlib
+XFree86 libraries.
+.It Li Xlk98
+XFree86 server link-kit for PC98 machines.
+.It Li Xlkit
+XFree86 server link-kit for standard machines.
+.It Li Xman
+XFree86 manual pages.
+.It Li Xprog
+XFree86 programmer's distribution.
+.It Li Xps
+XFree86 postscript documentation.
+.It Li Xset
+XFree86 graphical setup tool.
+.It Li PC98-Servers/X9480
+XFree86 PC98 8-bit (256 color) PEGC-480 server.
+.It Li PC98-Servers/X9EGC
+XFree86 PC98 4-bit (16 color) EGC server.
+.It Li PC98-Servers/X9GA9
+XFree86 PC98 GA-968V4/PCI (S3 968) server.
+.It Li PC98-Servers/X9GAN
+XFree86 PC98 GANB-WAP (cirrus) server.
+.It Li PC98-Servers/X9LPW
+XFree86 PC98 PowerWindowLB (S3) server.
+.It Li PC98-Servers/X9MGA
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9NKV
+XFree86 PC98 NKV-NEC (cirrus) server.
+.It Li PC98-Servers/X9NS3
+XFree86 PC98 NEC (S3) server.
+.It Li PC98-Servers/X9SPW
+XFree86 PC98 SKB-PowerWindow (S3) server.
+.It Li PC98-Servers/X9SVG
+[DESCRIPTION MISSING]
+.It Li PC98-Servers/X9TGU
+XFree86 PC98 Cyber9320 and TGUI9680 server.
+.It Li PC98-Servers/X9WEP
+XFree86 PC98 WAB-EP (cirrus) server.
+.It Li PC98-Servers/X9WS
+XFree86 PC98 WABS (cirrus) server.
+.It Li PC98-Servers/X9WSN
+XFree86 PC98 WSN-A2F (cirrus) server.
+.It Li Servers/X3DL
+XFree86 3D Labs server.
+.It Li Servers/X8514
+XFree86 8514 server.
+.It Li Servers/XAGX
+XFree86 8 bit AGX server.
+.It Li Servers/XI128
+XFree86 #9 Imagine I128 server.
+.It Li Servers/XMa8
+XFree86 ATI Mach8 server.
+.It Li Servers/XMa32
+XFree86 ATI Mach32 server.
+.It Li Servers/XMa64
+XFree86 ATI Mach64 server.
+.It Li Servers/XMono
+XFree86 monochrome server.
+.It Li Servers/XP9K
+XFree86 P9000 server.
+.It Li Servers/XS3
+XFree86 S3 server.
+.It Li Servers/XS3V
+XFree86 S3 Virge server.
+.It Li Servers/XSVGA
+XFree86 SVGA server.
+.It Li Servers/XVG16
+XFree86 VGA16 server.
+.It Li Servers/XW32
+XFree86 ET4000/W32, /W32i and /W32p server.
+.It Li Servers/XTGA
+Server for TGA cards (alpha architecture only).
+.It Li Servers/Xnest
+XFree86 nested X server.
+.It Li Servers/Xvfb
+XFree86 virtual frame-buffer X server.
+.It Li Xfnts
+XFree86 base font set.
+.It Li Xf100
+XFree86 100DPI font set.
+.It Li Xfcyr
+XFree86 Cyrillic font set.
+.It Li Xfscl
+XFree86 scalable font set.
+.It Li Xfnon
+XFree86 non-english font set.
+.It Li Xfsrv
+XFree86 font server.
+.El
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetXUser
+Selects the standard X user's 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 distSetCRYPTO
+Interactively select encryption subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distSetXF86
+Interactively select XFree86 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 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 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 mediaSetTape
+Select a tape device as the installation media.
+.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're 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're
+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're 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 and terminate sysinstall.
+.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's 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 = 4.6" .
+.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 Alpha packages on an i386 machine.
+For example,
+.Dq Li "CD_MACHINE_ARCH = alpha" .
+.It Va 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 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 BUGS
+This utility is a prototype which lasted several years past
+its expiration date and is greatly in need of death.
+.Sh AUTHORS
+.An Jordan K. Hubbard Aq jkh@FreeBSD.org
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.0 .
diff --git a/usr.sbin/sysinstall/sysinstall.h b/usr.sbin/sysinstall/sysinstall.h
new file mode 100644
index 0000000..938cfad
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.h
@@ -0,0 +1,885 @@
+/*
+ * 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 "libdisk.h"
+#include "dist.h"
+
+/*** Defines ***/
+
+#if defined(__i386__) || defined(__alpha__) || defined(__amd64__)
+#define WITH_SYSCONS
+#define WITH_MICE
+#endif
+
+#if defined(__i386__) || defined(__amd64__)
+#define WITH_SLICES
+#endif
+
+#if defined(__i386__) || defined(__alpha__)
+#define WITH_LINUX
+#endif
+
+#if defined(PC98)
+#define PCCARD_ARCH 1 /* Support PCCARD installations */
+#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_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_CRYPTO "distCRYPTO"
+#define VAR_DIST_SRC "distSRC"
+#define VAR_DIST_X11 "distX11"
+#define VAR_DIST_XSERVER "distXserver"
+#define VAR_DIST_XFONTS "distXfonts"
+#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_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_OSF1_ENABLE "osf1_enable"
+#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_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_TRY_DHCP "tryDHCP"
+#define VAR_TRY_RTSOL "tryRTSOL"
+#define VAR_SKIP_PCCARD "skipPCCARD"
+#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_XF86_CONFIG "_xf86config"
+#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" */
+#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;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_FLOPPY,
+ DEVICE_TYPE_FTP,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_CDROM,
+ DEVICE_TYPE_TAPE,
+ 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)();
+} 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 */
+ 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 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 Device *mediaDevice; /* Where we're getting our distribution from */
+extern unsigned int Dists; /* Which distributions we want */
+extern unsigned int CRYPTODists; /* Which naughty distributions we want */
+extern unsigned int SrcDists; /* Which src distributions we want */
+extern unsigned int XF86Dists; /* Which XFree86 dists we want */
+extern unsigned int XF86ServerDists; /* The XFree86 servers we want */
+extern unsigned int XF86FontDists; /* The XFree86 fonts we want */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+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 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 MenuMediaTape; /* Tape 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 MenuXF86; /* XFree86 main menu */
+extern DMenu MenuXF86Select; /* XFree86 distribution selection menu */
+extern DMenu MenuXF86SelectCore; /* XFree86 core distribution menu */
+extern DMenu MenuXF86SelectServer; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuXDesktops; /* Disk devices menu */
+extern DMenu MenuHTMLDoc; /* HTML Documentation menu */
+extern DMenu MenuUsermgmt; /* User management menu */
+extern DMenu MenuFixit; /* Fixit floppy/CDROM/shell menu */
+extern DMenu MenuXF86Config; /* Select XFree86 configuration type */
+extern int FixItMode; /* FixItMode starts shell onc urrent device (ie Serial port) */
+extern const char * StartName; /* Which name we were started as */
+
+/* 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 ***/
+
+/* 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);
+#ifdef __alpha__
+extern int configOSF1(dialogMenuItem *self);
+#endif
+extern int configUsers(dialogMenuItem *self);
+extern int configXSetup(dialogMenuItem *self);
+extern int configXDesktop(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_file_int(int);
+extern int dispatch_load_file(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 distSetXDeveloper(dialogMenuItem *self);
+extern int distSetKernDeveloper(dialogMenuItem *self);
+extern int distSetXKernDeveloper(dialogMenuItem *self);
+extern int distSetUser(dialogMenuItem *self);
+extern int distSetXUser(dialogMenuItem *self);
+extern int distSetMinimum(dialogMenuItem *self);
+extern int distSetEverything(dialogMenuItem *self);
+extern int distSetSrc(dialogMenuItem *self);
+extern int distSetXF86(dialogMenuItem *self);
+extern int distExtractAll(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 dmenuSetKmapVariable(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 *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 index_initialize(char *path);
+PkgNodePtr index_search(PkgNodePtr top, char *str, PkgNodePtr *tp);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **vtdev, Chunk **hdev);
+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 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 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 void mediaClose(void);
+extern int mediaTimeout(void);
+extern int mediaSetCDROM(dialogMenuItem *self);
+extern int mediaSetFloppy(dialogMenuItem *self);
+extern int mediaSetDOS(dialogMenuItem *self);
+extern int mediaSetTape(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);
+
+/* 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);
+
+/* 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);
+
+/* 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 void systemCreateHoloshell(void);
+extern int vsystem(char *fmt, ...) __printflike(1, 2);
+
+/* tape.c */
+extern char *mediaTapeBlocksize(void);
+extern Boolean mediaInitTape(Device *dev);
+extern FILE *mediaGetTape(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownTape(Device *dev);
+
+/* 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);
+
+/* usb.c */
+extern void usbInitialize(void);
+
+/* 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..65c353a
--- /dev/null
+++ b/usr.sbin/sysinstall/system.c
@@ -0,0 +1,542 @@
+/*
+ * 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 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 "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);
+}
+
+/*
+ * Harvest children if we are init.
+ */
+static void
+reap_children(int sig)
+{
+ int errbak = errno;
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+ errno = errbak;
+}
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ char *gunzip = RunningAsInit ? "/stand/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);
+
+ /* 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:/usr/X11R6/bin", 1);
+ setbuf(stdin, 0);
+ setbuf(stderr, 0);
+#ifdef __alpha__
+ i = 0;
+ sysctlbyname("machdep.unaligned_print", NULL, 0, &i, sizeof(i));
+#endif
+#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)
+ 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(__alpha__) || defined(__sparc64__)
+ reboot(RB_HALT);
+#else
+ reboot(0);
+#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/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.TXT", 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;
+
+ 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 (!RunningAsInit)
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/stand/sh", "/stand/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
+ it serial mode
+ since there is no
+ virtual console */
+ systemResumeDialog();
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/tape.c b/usr.sbin/sysinstall/tape.c
new file mode 100644
index 0000000..fd73da5
--- /dev/null
+++ b/usr.sbin/sysinstall/tape.c
@@ -0,0 +1,127 @@
+/*
+ * 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 tape media */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+
+static Boolean tapeInitted;
+
+char *
+mediaTapeBlocksize(void)
+{
+ char *cp = variable_get(VAR_TAPE_BLOCKSIZE);
+
+ return cp ? cp : DEFAULT_TAPE_BLOCKSIZE;
+}
+
+Boolean
+mediaInitTape(Device *dev)
+{
+ /* This is REALLY gross, but we need to do the init later in get due to the fact
+ * that media is initialized BEFORE a filesystem is mounted now.
+ */
+ return TRUE;
+}
+
+FILE *
+mediaGetTape(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX];
+ FILE *fp;
+
+ int i;
+
+ if (!tapeInitted) {
+ WINDOW *w = savescr();
+
+ msgDebug("Tape init routine called for %s (private dir is %s)\n",
+ dev->name, (char *)dev->private);
+ Mkdir(dev->private);
+ if (chdir(dev->private)) {
+ msgConfirm("Unable to CD to %s before extracting tape!\n"
+ "Tape media is not selected and thus cannot be installed from.",
+ (char *)dev->private);
+ return (FILE *)IO_ERROR;
+ }
+ /* We know the tape is already in the drive, so go for it */
+ msgNotify("First extracting distributions from %s...", dev->description);
+ if (!strcmp(dev->name, "rft0"))
+ i = vsystem("ft | cpio -idum %s --block-size %s", cpioVerbosity(), mediaTapeBlocksize());
+ else
+ i = vsystem("cpio -idum %s --block-size %s -I %s", cpioVerbosity(), mediaTapeBlocksize(), dev->devname);
+ if (!i) {
+ tapeInitted = TRUE;
+ msgDebug("Tape initialized successfully.\n");
+ }
+ else {
+ msgConfirm("Tape extract command failed with status %d!\n"
+ "Unable to use tape media.", i);
+ restorescr(w);
+ return (FILE *)IO_ERROR;
+ }
+ restorescr(w);
+ }
+
+ sprintf(buf, "%s/%s", (char *)dev->private, file);
+ if (isDebug())
+ msgDebug("Request for %s from tape (looking in %s)\n", file, buf);
+ if (file_readable(buf))
+ fp = fopen(buf, "r");
+ else {
+ sprintf(buf, "%s/releases/%s", (char *)dev->private, file);
+ fp = fopen(buf, "r");
+ }
+ /* Nuke the files behind us to save space */
+ if (fp)
+ unlink(buf);
+ return fp;
+}
+
+void
+mediaShutdownTape(Device *dev)
+{
+ if (!tapeInitted)
+ return;
+ if (file_readable((char *)dev->private)) {
+ msgDebug("Cleaning up results of tape extract in %s..\n",
+ (char *)dev->private);
+ (void)vsystem("rm -rf %s", (char *)dev->private);
+ }
+ tapeInitted = FALSE;
+}
diff --git a/usr.sbin/sysinstall/tcpip.c b/usr.sbin/sysinstall/tcpip.c
new file mode 100644
index 0000000..1d2534f
--- /dev/null
+++ b/usr.sbin/sysinstall/tcpip.c
@@ -0,0 +1,697 @@
+/*
+ * $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>
+
+/* 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 },
+ { 0 },
+};
+
+#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;
+
+ 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++ != '.')
+ return 0;
+ b = strtol(endptr, &endptr, 10);
+ if (*endptr++ != '.')
+ return 0;
+ c = strtol(endptr, &endptr, 10);
+ if (*endptr++ != '.')
+ return 0;
+ d = strtol(endptr, &endptr, 10);
+ if (*endptr != '\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;
+ unsigned 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 */
+ mask = strtoul(netmask, &endptr, 10);
+ if (!_validByte(mask) || *endptr++ != '.')
+ return 0;
+ 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)
+{
+ /* If it fails, do it the old-fashioned way */
+ if (dhcpParseLeases("/var/db/dhclient.leases", 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...");
+ vsystem("dhclient -r %s", devp->name);
+ if (0 == vsystem("dhclient -1 %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, "lp", 2))
+ 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];
+#ifdef PCCARD_ARCH
+ char *pccard;
+#endif
+ 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)
+ sprintf(temp, "DHCP");
+ else
+ sprintf(temp, "inet %s %s netmask %s",
+ ipaddr, extras, netmask);
+ variable_set2(ifn, temp, 1);
+ }
+#ifdef PCCARD_ARCH
+ pccard = variable_get("_pccard_install");
+ if (pccard && strcmp(pccard, "YES") == 0 && ipv4_enable) {
+ variable_set2("pccard_ifconfig", temp, 1);
+ }
+#endif
+ 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..1d8e047
--- /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, 80, 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, 80, stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+ *termcapp = (char *)termcap_ansi;
+ }
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct ttysize ts;
+
+ 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, TIOCGSIZE, &ts) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ts.ts_lines = 0;
+ }
+ StatusLine = ts.ts_lines ? ts.ts_lines - 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/usb.c b/usr.sbin/sysinstall/usb.c
new file mode 100644
index 0000000..4eedbd5
--- /dev/null
+++ b/usr.sbin/sysinstall/usb.c
@@ -0,0 +1,44 @@
+/*
+ * USB support for sysinstall
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 2000 John Baldwin <jhb@FreeBSD.org>. All rights reserved.
+ *
+ * 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
+ * use.
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/time.h>
+
+void
+usbInitialize(void)
+{
+ int fd;
+ WINDOW *w;
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ if ((fd = open("/dev/usb", O_RDONLY)) < 0) {
+ msgDebug("Can't open USB controller.\n");
+ return;
+ }
+ close(fd);
+
+ w = savescr();
+ msgNotify("Initializing USB controller....");
+
+ variable_set2("usbd_enable", "YES", 1);
+
+ vsystem("/stand/usbd");
+ restorescr(w);
+}
diff --git a/usr.sbin/sysinstall/user.c b/usr.sbin/sysinstall/user.c
new file mode 100644
index 0000000..ecee440
--- /dev/null
+++ b/usr.sbin/sysinstall/user.c
@@ -0,0 +1,742 @@
+/*
+ * $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],
+ 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 - 2
+
+/* 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 },
+ { 0 },
+};
+
+/* The user configuration menu. */
+static Layout userLayout[] = {
+#define LAYOUT_UNAME 0
+ { 3, 6, UT_NAMESIZE, UT_NAMESIZE + 1,
+ "Login ID:", "The login name of the new user (mandatory)",
+ uname, STRINGOBJ, NULL },
+#define LAYOUT_UID 1
+ { 3, 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
+ { 3, 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
+ { 3, 43, 15, PASSWD_FIELD_LEN - 1,
+ "Password:", "The password for this user (enter this field with care!)",
+ passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
+#define LAYOUT_GECOS 4
+ { 8, 6, 33, GECOS_FIELD_LEN - 1,
+ "Full name:", "The user's full name (comment)",
+ gecos, STRINGOBJ, NULL },
+#define LAYOUT_UMEMB 5
+ { 8, 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 6
+ { 13, 6, 20, HOMEDIR_FIELD_LEN - 1,
+ "Home directory:", "The user's home directory (leave blank for default)",
+ homedir, STRINGOBJ, NULL },
+#define LAYOUT_SHELL 7
+ { 13, 29, 29, SHELL_FIELD_LEN - 1,
+ "Login shell:", "The user's login shell (leave blank for default)",
+ shell, STRINGOBJ, NULL },
+#define LAYOUT_U_OKBUTTON 8
+ { 18, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_U_CANCELBUTTON 9
+ { 18, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ { 0 },
+};
+
+/* 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 ((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 + 22, " Add a new user ");
+
+ CLEAR(uname);
+ CLEAR(uid);
+ CLEAR(ugroup);
+ CLEAR(gecos);
+ CLEAR(passwd);
+ 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..e86104f
--- /dev/null
+++ b/usr.sbin/sysinstall/variable.c
@@ -0,0 +1,326 @@
+/*
+ * 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 sysinstall variables to file..\n");
+
+ fp = fopen("/etc/sysinstall.vars", "w");
+ if (!fp) {
+ msgConfirm("Unable to write to /etc/sysinstall.vars: %s",
+ 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 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);
+ putenv(tmp);
+}
+
+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..3a07887
--- /dev/null
+++ b/usr.sbin/sysinstall/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 "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+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;
+}
+
+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..028fda5
--- /dev/null
+++ b/usr.sbin/syslogd/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.bin/wall
+
+PROG= syslogd
+MAN= syslog.conf.5 syslogd.8
+SRCS= syslogd.c ttymsg.c
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+WARNS?= 1
+CFLAGS+=-DINET6 -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..2dc61a8
--- /dev/null
+++ b/usr.sbin/syslogd/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * 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_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..789d63f
--- /dev/null
+++ b/usr.sbin/syslogd/syslog.conf.5
@@ -0,0 +1,487 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD$
+.\"
+.Dd June 9, 1993
+.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 along on the line),
+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: auth, authpriv, console, cron, daemon, ftp, kern,
+lpr, mail, mark, news, ntp, security, syslog, user, uucp and local0 through
+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): emerg, alert, crit, err,
+warning, notice, info and 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-seprarated
+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's 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 doesn't 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.
+.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 didn't 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's 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.
+.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 /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 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 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.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr syslogd 8
diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8
new file mode 100644
index 0000000..80782d6
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.8
@@ -0,0 +1,331 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)syslogd.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd November 24, 2001
+.Dt SYSLOGD 8
+.Os
+.Sh NAME
+.Nm syslogd
+.Nd log systems messages
+.Sh SYNOPSIS
+.Nm
+.Op Fl 46Acdknosuv
+.Op Fl a Ar allowed_peer
+.Op Fl b Ar bind_address
+.Op Fl f Ar config_file
+.Op Fl l 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 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
+.Ar Allowed_peer
+can 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
+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 l
+Specify a location where
+.Nm
+should place an additional log socket.
+Up to 19 additional logging sockets can be specified.
+The primary use for this is to place additional log sockets in
+.Pa /var/run/log
+of various chroot filespaces.
+.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 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 socket
+.Pa /var/run/log ,
+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;
+therefore, they must be created manually before running
+.Nm .
+.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 /dev/klog
+kernel log device
+.El
+.Sh SEE ALSO
+.Xr logger 1 ,
+.Xr syslog 3 ,
+.Xr services 5 ,
+.Xr syslog.conf 5
+.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 doesn't 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..367901c
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.c
@@ -0,0 +1,2520 @@
+/*
+ * 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.
+ * 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, 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>
+
+#ifdef NI_WITHSCOPEID
+static const int withscopeid = NI_WITHSCOPEID;
+#else
+static const int withscopeid;
+#endif
+
+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 */
+
+#define MAXFUNIX 20
+
+int nfunix = 1;
+const char *funixn[MAXFUNIX] = { _PATH_LOG };
+int funix[MAXFUNIX];
+
+/*
+ * 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
+};
+
+/*
+ * 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 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 char bootfile[MAXLINE+1]; /* booted kernel file */
+
+struct allowedpeer *AllowedPeers; /* List of allowed peers */
+static int NumAllowed; /* Number of entries in AllowedPeers */
+
+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 */
+
+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 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 *);
+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 *);
+static int waitdaemon(int, int, int);
+static void timedout(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;
+ FILE *fp;
+ char line[MAXLINE + 1];
+ const char *bindhostname, *hname;
+ struct timeval tv, *tvp;
+ struct sigaction sact;
+ sigset_t mask;
+ pid_t ppid = 1;
+ socklen_t len;
+
+ bindhostname = NULL;
+ while ((ch = getopt(argc, argv, "46Aa:b:cdf:kl:m:nop:P:suv")) != -1)
+ switch (ch) {
+ case '4':
+ family = PF_INET;
+ break;
+#ifdef INET6
+ case '6':
+ family = PF_INET6;
+ break;
+#endif
+ 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 'd': /* debug */
+ Debug++;
+ break;
+ case 'f': /* configuration file */
+ ConfFile = optarg;
+ break;
+ case 'k': /* keep remote kern fac */
+ KeepKernFac = 1;
+ break;
+ case 'l':
+ if (strlen(optarg) >= sizeof(sunx.sun_path))
+ errx(1, "%s path too long, exiting", optarg);
+ if (nfunix < MAXFUNIX)
+ funixn[nfunix++] = optarg;
+ else
+ warnx("out of descriptors, ignoring %s",
+ optarg);
+ 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);
+ funixn[0] = optarg;
+ break;
+ case 'P': /* path for alt. PID */
+ PidFile = optarg;
+ break;
+ case 's': /* no network mode */
+ SecureMode++;
+ break;
+ case 'u': /* only log specified priority */
+ UniquePriority++;
+ break;
+ case 'v': /* log facility and priority */
+ LogFacPri++;
+ break;
+ default:
+ usage();
+ }
+ if ((argc -= optind) != 0)
+ usage();
+
+ if (!Debug) {
+ ppid = waitdaemon(0, 0, 30);
+ if (ppid < 0)
+ err(1, "could not become daemon");
+ } 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
+ for (i = 0; i < nfunix; i++) {
+ (void)unlink(funixn[i]);
+ memset(&sunx, 0, sizeof(sunx));
+ sunx.sun_family = AF_UNIX;
+ (void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
+ funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (funix[i] < 0 ||
+ bind(funix[i], (struct sockaddr *)&sunx,
+ SUN_LEN(&sunx)) < 0 ||
+ chmod(funixn[i], 0666) < 0) {
+ (void)snprintf(line, sizeof line,
+ "cannot create %s", funixn[i]);
+ logerror(line);
+ dprintf("cannot create %s (%d)\n", funixn[i], errno);
+ if (i == 0)
+ die(0);
+ }
+ }
+ 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 */
+ fp = fopen(PidFile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ (void)fclose(fp);
+ }
+
+ 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];
+ }
+ }
+ for (i = 0; i < nfunix; i++) {
+ if (funix[i] != -1 && funix[i] > fdsrmax)
+ fdsrmax = funix[i];
+ }
+
+ 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);
+ }
+ }
+ for (i = 0; i < nfunix; i++) {
+ if (funix[i] != -1)
+ FD_SET(funix[i], fdsr);
+ }
+
+ i = select(fdsrmax+1, fdsr, NULL, NULL, tvp);
+ switch (i) {
+ case 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);
+ } else if (l < 0 && errno != EINTR)
+ logerror("recvfrom inet");
+ }
+ }
+ }
+ for (i = 0; i < nfunix; i++) {
+ if (funix[i] != -1 && FD_ISSET(funix[i], fdsr)) {
+ len = sizeof(fromunix);
+ l = recvfrom(funix[i], line, MAXLINE, 0,
+ (struct sockaddr *)&fromunix, &len);
+ if (l > 0) {
+ line[l] = '\0';
+ printline(LocalHostName, line);
+ } 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 [-46Acdknosuv] [-a allowed_peer]",
+ " [-b bind address] [-f config_file]",
+ " [-l log_socket] [-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)
+{
+ 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 ((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, 0);
+}
+
+/*
+ * 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);
+ prilev = LOG_PRI(pri);
+
+ /* extract program name */
+ for (i = 0; i < NAME_MAX; i++) {
+ if (!isprint(msg[i]) || msg[i] == ':' || msg[i] == '[' ||
+ 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;
+ f->f_file = open(ctty, O_WRONLY, 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 &&
+ !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
+fprintlog(struct filed *f, int flags, const char *msg)
+{
+ struct iovec iov[7];
+ 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;
+ v->iov_len = snprintf(greetings, sizeof greetings,
+ "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
+ f->f_prevhost, ctime(&now));
+ 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 = 15;
+ 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 {
+ v->iov_base = f->f_prevline;
+ v->iov_len = f->f_prevlen;
+ }
+ v++;
+
+ dprintf("Logging to %s", TypeNames[f->f_type]);
+ f->f_time = now;
+
+ switch (f->f_type) {
+ case F_UNUSED:
+ dprintf("\n");
+ break;
+
+ case F_FORW:
+ 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 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, 7) < 0) {
+ 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))
+ (void)fsync(f->f_file);
+ 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, 7) < 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, 7, 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);
+ break;
+ }
+ f->f_prevcount = 0;
+ if (msg)
+ 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)
+{
+ 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, 7, 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, 7, 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 | withscopeid);
+ 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 | withscopeid);
+ 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;
+ int was_initialized;
+ char buf[100];
+ int i;
+
+ 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);
+ }
+ for (i = 0; i < nfunix; i++)
+ if (funixn[i] && funix[i] != -1)
+ (void)unlink(funixn[i]);
+ 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 != ',')
+ 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]))
+ break;
+ prog[i] = p[i];
+ }
+ prog[i] = 0;
+ continue;
+ }
+ 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) {
+ 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:
+ 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 + 1;
+ 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 '@':
+ (void)strlcpy(f->f_un.f_forw.f_hname, ++p,
+ sizeof(f->f_un.f_forw.f_hname));
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(f->f_un.f_forw.f_hname, "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, O_WRONLY|O_APPEND, 0)) < 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_fname, p + 1, sizeof(f->f_un.f_fname));
+ 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, i;
+ struct addrinfo hints, *res;
+ struct in_addr *addrp, *maskp;
+ u_int32_t *addr6p, *mask6p;
+ 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 | withscopeid);
+ printf("addr = %s, ", ip);
+ getnameinfo((struct sockaddr *)&ap.a_mask,
+ ((struct sockaddr *)&ap.a_mask)->sa_len,
+ ip, sizeof ip, NULL, 0,
+ NI_NUMERICHOST | withscopeid);
+ 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, j, reject;
+ 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;
+ struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL;
+ 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 | withscopeid | 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;
+#ifdef NI_WITHSCOPEID
+ 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;
+ }
+#endif
+ 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) {
+ *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (*s < 0) {
+ logerror("socket");
+ continue;
+ }
+ if (r->ai_family == AF_INET6) {
+ int on = 1;
+ if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (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;
+ }
+
+ (*socks)++;
+ s++;
+ }
+
+ if (*socks == 0) {
+ free(socks);
+ if (Debug)
+ return (NULL);
+ else
+ die(0);
+ }
+ if (res)
+ freeaddrinfo(res);
+
+ return (socks);
+}
diff --git a/usr.sbin/tcpdchk/Makefile b/usr.sbin/tcpdchk/Makefile
new file mode 100644
index 0000000..57938e1
--- /dev/null
+++ b/usr.sbin/tcpdchk/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.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 !defined(NOINET6)
+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..bea68aa
--- /dev/null
+++ b/usr.sbin/tcpdmatch/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+.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 !defined(NOINET6)
+CFLAGS+=-DINET6
+.endif
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/Makefile b/usr.sbin/tcpdump/Makefile
new file mode 100644
index 0000000..c549530
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 0.1 (RGrimes) 4/4/93
+# $FreeBSD$
+
+SUBDIR= tcpdump tcpslice
+
+.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..bbdb21b
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/Makefile
@@ -0,0 +1,57 @@
+# $FreeBSD$
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+.PATH: ${TCPDUMP_DISTDIR}
+
+PROG= tcpdump
+SRCS= addrtoname.c gmpls.c gmt2local.c machdep.c oui.c parsenfsfh.c \
+ print-802_11.c print-ah.c print-aodv.c print-ap1394.c \
+ print-arcnet.c print-arp.c print-ascii.c print-atalk.c \
+ print-atm.c print-bfd.c print-bgp.c \
+ print-bootp.c print-beep.c print-cdp.c print-chdlc.c \
+ print-cip.c print-cnfp.c print-decnet.c print-domain.c \
+ print-dvmrp.c print-egp.c print-enc.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-ipfc.c print-ipcomp.c \
+ print-ipx.c print-isakmp.c print-isoclns.c print-krb.c \
+ print-l2tp.c print-lane.c print-ldp.c print-llc.c \
+ print-lwres.c print-msdp.c print-mobile.c print-mpls.c \
+ print-netbios.c print-nfs.c print-ntp.c print-null.c \
+ print-ospf.c print-pflog.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-sl.c print-sll.c print-smb.c print-snmp.c \
+ print-stp.c print-sunatm.c print-sunrpc.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 smbutil.c tcpdump.c util.c version.c
+CLEANFILES+= version.c
+
+CFLAGS+= -I${.CURDIR} -I${TCPDUMP_DISTDIR}
+CFLAGS+= -DHAVE_CONFIG_H
+CFLAGS+= -D_U_="__attribute__((unused))"
+
+.if !defined(NOINET6)
+SRCS+= print-ip6.c print-ip6opts.c print-ripng.c print-icmp6.c \
+ print-frag6.c print-rt6.c print-ospf6.c print-dhcp6.c print-mobility.c
+CFLAGS+= -DINET6
+.endif
+.if ${MACHINE_ARCH} != "i386"
+CFLAGS+= -DLBL_ALIGN
+.endif
+
+DPADD= ${LIBL} ${LIBPCAP}
+LDADD= -ll -lpcap
+.if !defined(NOCRYPT) && !defined(NO_OPENSSL) && !defined(RELEASE_CRUNCH)
+DISTRIBUTION=crypto
+DPADD+= ${LIBCRYPTO}
+LDADD+= -lcrypto
+CFLAGS+= -I${DESTDIR}/usr/include/openssl -DHAVE_LIBCRYPTO -DHAVE_RC5_H -DHAVE_CAST_H -DHAVE_OPENSSL_EVP_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..5ab8348
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/config.h
@@ -0,0 +1,294 @@
+/* $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 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 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 sized types */
+/* XXX this should move to the more standard uint*_t */
+/* #undef int8_t */
+/* #undef int16_t */
+/* #undef int32_t */
+/* #undef u_int16_t */
+/* #undef u_int32_t */
+/* #undef u_int8_t */
+
+/* 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 to 1 if you have the `bpf_dump' function. */
+#define HAVE_BPF_DUMP 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). */
+/* #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. */
+/* #undef HAVE_NETINET_IF_ETHER_H */
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+/* #define 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 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 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 a `char', as computed by sizeof. */
+#define SIZEOF_CHAR 1
+
+/* The size of a `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* The size of a `long', as computed by sizeof. */
+#define SIZEOF_LONG 4
+
+/* The size of a `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
diff --git a/usr.sbin/tcpdump/tcpslice/Makefile b/usr.sbin/tcpdump/tcpslice/Makefile
new file mode 100644
index 0000000..2c44459
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/Makefile
@@ -0,0 +1,23 @@
+# @(#)Makefile 0.1 (RWGrimes) 3/24/93
+# $FreeBSD$
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+
+PROG= tcpslice
+SRCS= gwtm2secs.c search.c tcpslice.c util.c version.c version.h
+CLEANFILES= version.c version.h
+
+CFLAGS+= -I.
+
+DPADD= ${LIBPCAP}
+LDADD= -lpcap
+
+.ORDER: version.c version.h
+version.c version.h: ${TCPDUMP_DISTDIR}/VERSION
+ rm -f version.c ; \
+ sed 's/.*/char version[] = "&";/' ${TCPDUMP_DISTDIR}/VERSION > version.c
+ set `sed 's/\([0-9]*\)\.\([0-9]*\).*/\1 \2/' ${TCPDUMP_DISTDIR}/VERSION` ; \
+ { echo '#define VERSION_MAJOR' $$1 ; \
+ echo '#define VERSION_MINOR' $$2 ; } > version.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/tcpslice/gwtm2secs.c b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c
new file mode 100644
index 0000000..249454e
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names 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.
+ */
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#)$FreeBSD$ (LBL)";
+#endif
+
+/*
+ * gwtm2secs.c - convert "tm" structs for Greenwich time to Unix timestamp
+ */
+
+#include "tcpslice.h"
+
+static int days_in_month[] =
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+#define IS_LEAP_YEAR(year) \
+ (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+
+time_t gwtm2secs( struct tm *tm )
+ {
+ int i, days, year;
+
+ year = tm->tm_year;
+
+ /* Allow for year being specified with either 2 digits or 4 digits.
+ * 2-digit years are either 19xx or 20xx - a simple heuristic
+ * distinguishes them, since we can't represent any time < 1970.
+ */
+ if ( year < 100 )
+ if ( year >= 70 )
+ year += 1900;
+ else
+ year += 2000;
+
+ days = 0;
+ for ( i = 1970; i < year; ++i )
+ {
+ days += 365;
+ if ( IS_LEAP_YEAR(i) )
+ ++days;
+ }
+
+ for ( i = 0; i < tm->tm_mon; ++i )
+ days += days_in_month[i];
+
+ if ( IS_LEAP_YEAR(year) && tm->tm_mon > 1 ) /* 1 is February */
+ ++days;
+
+ days += tm->tm_mday - 1; /* -1 since days are numbered starting at 1 */
+
+ return days * 86400 + tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
+ }
diff --git a/usr.sbin/tcpdump/tcpslice/search.c b/usr.sbin/tcpdump/tcpslice/search.c
new file mode 100644
index 0000000..a460ffa
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/search.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 1990, 1991, 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: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names 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.
+ */
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#)$FreeBSD$ (LBL)";
+#endif
+
+/*
+ * search.c - supports fast searching through tcpdump files for timestamps
+ */
+
+#include "tcpslice.h"
+
+
+/* Maximum number of seconds that we can conceive of a dump file spanning. */
+#define MAX_REASONABLE_FILE_SPAN (3600*24*366) /* one year */
+
+/* Maximum packet length we ever expect to see. */
+#define MAX_REASONABLE_PACKET_LENGTH 65535
+
+/* Size of a packet header in bytes; easier than typing the sizeof() all
+ * the time ...
+ */
+#define PACKET_HDR_LEN (sizeof( struct pcap_pkthdr ))
+
+extern int snaplen;
+
+/* The maximum size of a packet, including its header. */
+#define MAX_PACKET_SIZE (PACKET_HDR_LEN + snaplen)
+
+/* Number of contiguous bytes from a dumpfile in which there's guaranteed
+ * to be enough information to find a "definite" header if one exists
+ * therein. This takes 3 full packets - the first to be just misaligned
+ * (one byte short of a full packet), missing its timestamp; the second
+ * to have the legitimate timestamp; and the third to provide confirmation
+ * that the second is legit, making it a "definite" header. We could
+ * scrimp a bit here since not the entire third packet is required, but
+ * it doesn't seem worth it
+ */
+#define MAX_BYTES_FOR_DEFINITE_HEADER (3 * MAX_PACKET_SIZE)
+
+/* Maximum number of seconds that might reasonably separate two headers. */
+#define MAX_REASONABLE_HDR_SEPARATION (3600 * 24 * 7) /* one week */
+
+/* When searching a file for a packet, if we think we're within this many
+ * bytes of the packet we just search linearly. Since linear searches are
+ * probably much faster than random ones (random ones require searching for
+ * the beginning of the packet, which may be unaligned in memory), we make
+ * this value pretty hefty.
+ */
+#define STRAIGHT_SCAN_THRESHOLD (100 * MAX_PACKET_SIZE)
+
+
+/* Given a header and an acceptable first and last time stamp, returns non-zero
+ * if the header looks reasonable and zero otherwise.
+ */
+static int
+reasonable_header( struct pcap_pkthdr *hdr, long first_time, long last_time )
+ {
+ if ( last_time == 0 )
+ last_time = first_time + MAX_REASONABLE_FILE_SPAN;
+
+ return hdr->ts.tv_sec >= first_time &&
+ hdr->ts.tv_sec <= last_time &&
+ hdr->len > 0 &&
+ hdr->len <= MAX_REASONABLE_PACKET_LENGTH &&
+ hdr->caplen > 0 &&
+ hdr->caplen <= MAX_REASONABLE_PACKET_LENGTH;
+ }
+
+
+#define SWAPLONG(y) \
+((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
+#define SWAPSHORT(y) \
+ ( (((y)&0xff)<<8) | (((y)&0xff00)>>8) )
+
+/* Given a buffer, extracts a (properly aligned) packet header from it. */
+
+static void
+extract_header( pcap_t *p, u_char *buf, struct pcap_pkthdr *hdr )
+ {
+ bcopy((char *) buf, (char *) hdr, sizeof(struct pcap_pkthdr));
+
+ if ( pcap_is_swapped( p ) )
+ {
+ hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
+ hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
+ hdr->len = SWAPLONG(hdr->len);
+ hdr->caplen = SWAPLONG(hdr->caplen);
+ }
+
+ /*
+ * From bpf/libpcap/savefile.c:
+ *
+ * We interchanged the caplen and len fields at version 2.3,
+ * in order to match the bpf header layout. But unfortunately
+ * some files were written with version 2.3 in their headers
+ * but without the interchanged fields.
+ */
+ if ( pcap_minor_version( p ) < 3 ||
+ (pcap_minor_version( p ) == 3 && hdr->caplen > hdr->len) )
+ {
+ int t = hdr->caplen;
+ hdr->caplen = hdr->len;
+ hdr->len = t;
+ }
+ }
+
+
+/* Search a buffer to locate the first header within it. Return values
+ * are HEADER_NONE, HEADER_CLASH, HEADER_PERHAPS, and HEADER_DEFINITELY.
+ * The first indicates that no evidence of a header was found; the second
+ * that two or more possible headers were found, neither more convincing
+ * than the other(s); the third that exactly one "possible" header was
+ * found; and the fourth that exactly one "definite" header was found.
+ *
+ * Headers are detected by looking for positions in the buffer which have
+ * reasonable timestamps and lengths. If there is enough room in the buffer
+ * for another header to follow a candidate header, a check is made for
+ * that following header. If it is present then the header is *definite*
+ * (unless another "perhaps" or "definite" header is found); if not, then
+ * the header is discarded. If there is not enough room in the buffer for
+ * another header then the candidate is *perhaps* (unless another header
+ * is subsequently found). A "tie" between a "definite" header and a
+ * "perhaps" header is resolved in favor of the definite header. Any
+ * other tie leads to HEADER_CLASH.
+ *
+ * The buffer position of the header is returned in hdrpos_addr and
+ * for convenience the corresponding header in return_hdr.
+ *
+ * first_time is the earliest possible acceptable timestamp in the
+ * header. last_time, if non-zero, is the last such timestamp. If
+ * zero, then up to MAX_REASONABLE_FILE_SPAN seconds after first_time
+ * is acceptable.
+ */
+
+#define HEADER_NONE 0
+#define HEADER_CLASH 1
+#define HEADER_PERHAPS 2
+#define HEADER_DEFINITELY 3
+
+static int
+find_header( pcap_t *p, u_char *buf, int buf_len,
+ long first_time, long last_time,
+ u_char **hdrpos_addr, struct pcap_pkthdr *return_hdr )
+ {
+ u_char *bufptr, *bufend, *last_pos_to_try;
+ struct pcap_pkthdr hdr, hdr2;
+ int status = HEADER_NONE;
+ int saw_PERHAPS_clash = 0;
+
+ /* Initially, try each buffer position to see whether it looks like
+ * a valid packet header. We may later restrict the positions we look
+ * at to avoid seeing a sequence of legitimate headers as conflicting
+ * with one another.
+ */
+ bufend = buf + buf_len;
+ last_pos_to_try = bufend - PACKET_HDR_LEN;
+
+ for ( bufptr = buf; bufptr < last_pos_to_try; ++bufptr )
+ {
+ extract_header( p, bufptr, &hdr );
+
+ if ( reasonable_header( &hdr, first_time, last_time ) )
+ {
+ u_char *next_header = bufptr + PACKET_HDR_LEN + hdr.caplen;
+
+ if ( next_header + PACKET_HDR_LEN < bufend )
+ { /* check for another good header */
+ extract_header( p, next_header, &hdr2 );
+
+ if ( reasonable_header( &hdr2, hdr.ts.tv_sec,
+ hdr.ts.tv_sec + MAX_REASONABLE_HDR_SEPARATION ) )
+ { /* a confirmed header */
+ switch ( status )
+ {
+ case HEADER_NONE:
+ case HEADER_PERHAPS:
+ status = HEADER_DEFINITELY;
+ *hdrpos_addr = bufptr;
+ *return_hdr = hdr;
+
+ /* Make sure we don't demote this "definite"
+ * to a "clash" if we stumble across its
+ * successor.
+ */
+ last_pos_to_try = next_header - PACKET_HDR_LEN;
+ break;
+
+ case HEADER_DEFINITELY:
+ return HEADER_CLASH;
+
+ default:
+ error( "bad status in find_header()" );
+ }
+ }
+
+ /* ... else the header is bogus - we've verified that it's
+ * not followed by a reasonable header.
+ */
+ }
+
+ else
+ { /* can't check for another good header */
+ switch ( status )
+ {
+ case HEADER_NONE:
+ status = HEADER_PERHAPS;
+ *hdrpos_addr = bufptr;
+ *return_hdr = hdr;
+ break;
+
+ case HEADER_PERHAPS:
+ /* We don't immediately turn this into a
+ * clash because perhaps we'll later see a
+ * "definite" which will save us ...
+ */
+ saw_PERHAPS_clash = 1;
+ break;
+
+ case HEADER_DEFINITELY:
+ /* Keep the definite in preference to this one. */
+ break;
+
+ default:
+ error( "bad status in find_header()" );
+ }
+ }
+ }
+ }
+
+ if ( status == HEADER_PERHAPS && saw_PERHAPS_clash )
+ status = HEADER_CLASH;
+
+ return status;
+ }
+
+
+/* Positions the sf_readfile stream such that the next sf_read() will
+ * read the final full packet in the file. Returns non-zero if
+ * successful, zero if unsuccessful. If successful, returns the
+ * timestamp of the last packet in last_timestamp.
+ *
+ * Note that this routine is a special case of sf_find_packet(). In
+ * order to use sf_find_packet(), one first must use this routine in
+ * order to give sf_find_packet() an upper bound on the timestamps
+ * present in the dump file.
+ */
+int
+sf_find_end( pcap_t *p, struct timeval *first_timestamp,
+ struct timeval *last_timestamp )
+ {
+ long first_time = first_timestamp->tv_sec;
+ u_int num_bytes;
+ u_char *buf, *bufpos, *bufend;
+ u_char *hdrpos;
+ struct pcap_pkthdr hdr, successor_hdr;
+ int status;
+
+ /* Allow enough room for at least two full (untruncated) packets,
+ * perhaps followed by a truncated packet, so we have a shot at
+ * finding a "definite" header and following its chain to the
+ * end of the file.
+ */
+ num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
+ if ( fseek( pcap_file( p ), (long) -num_bytes, 2 ) < 0 )
+ return 0;
+
+ buf = (u_char *)malloc((u_int) num_bytes);
+ if ( ! buf )
+ return 0;
+
+ status = 0;
+ bufpos = buf;
+ bufend = buf + num_bytes;
+
+ if ( fread( (char *) bufpos, num_bytes, 1, pcap_file( p ) ) != 1 )
+ goto done;
+
+ if ( find_header( p, bufpos, num_bytes,
+ first_time, 0L, &hdrpos, &hdr ) != HEADER_DEFINITELY )
+ goto done;
+
+ /* Okay, we have a definite header in our hands. Follow its
+ * chain till we find the last valid packet in the file ...
+ */
+ for ( ; ; )
+ {
+ /* move to the next header position */
+ bufpos = hdrpos + PACKET_HDR_LEN + hdr.caplen;
+
+ /* bufpos now points to a candidate packet, which if valid
+ * should replace the current packet pointed to by hdrpos as
+ * the last valid packet ...
+ */
+ if ( bufpos >= bufend - PACKET_HDR_LEN )
+ /* not enough room for another header */
+ break;
+
+ extract_header( p, bufpos, &successor_hdr );
+
+ first_time = hdr.ts.tv_sec;
+ if ( ! reasonable_header( &successor_hdr, first_time, 0L ) )
+ /* this bodes ill - it means bufpos is perhaps a
+ * bogus packet header after all ...
+ */
+ break;
+
+ /* Note that the following test is for whether the next
+ * packet starts at a position > bufend, *not* for a
+ * position >= bufend. If this is the last packet in the
+ * file and there isn't a subsequent partial packet, then
+ * we expect the first buffer position beyond this packet
+ * to be just beyond the end of the buffer, i.e., at bufend
+ * itself.
+ */
+ if ( bufpos + PACKET_HDR_LEN + successor_hdr.caplen > bufend )
+ /* the packet is truncated */
+ break;
+
+ /* Accept this packet as fully legit. */
+ hdrpos = bufpos;
+ hdr = successor_hdr;
+ }
+
+ /* Success! Last valid packet is at hdrpos. */
+ *last_timestamp = hdr.ts;
+ status = 1;
+
+ /* Seek so that the next read will start at last valid packet. */
+ if ( fseek( pcap_file( p ), (long) -(bufend - hdrpos), 2 ) < 0 )
+ error( "final fseek() failed in sf_find_end()" );
+
+ done:
+ free( (char *) buf );
+
+ return status;
+ }
+
+
+/* Takes two timeval's and returns the difference, tv2 - tv1, as a double. */
+
+static double
+timeval_diff( struct timeval *tv1, struct timeval *tv2 )
+ {
+ double result = (tv2->tv_sec - tv1->tv_sec);
+ result += (tv2->tv_usec - tv1->tv_usec) / 1000000.0;
+
+ return result;
+ }
+
+
+/* Returns true if timestamp t1 is chronologically less than timestamp t2. */
+
+int
+sf_timestamp_less_than( struct timeval *t1, struct timeval *t2 )
+ {
+ return t1->tv_sec < t2->tv_sec ||
+ (t1->tv_sec == t2->tv_sec &&
+ t1->tv_usec < t2->tv_usec);
+ }
+
+
+/* Given two timestamps on either side of desired_time and their positions,
+ * returns the interpolated position of the desired_time packet. Returns a
+ * negative value if the desired_time is outside the given range.
+ */
+
+static long
+interpolated_position( struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time )
+ {
+ double full_span = timeval_diff( max_time, min_time );
+ double desired_span = timeval_diff( desired_time, min_time );
+ long full_span_pos = max_pos - min_pos;
+ double fractional_offset = desired_span / full_span;
+
+ if ( fractional_offset < 0.0 || fractional_offset > 1.0 )
+ return -1;
+
+ return min_pos + (long) (fractional_offset * (double) full_span_pos);
+ }
+
+
+/* Reads packets linearly until one with a time >= the given desired time
+ * is found; positions the dump file so that the next read will start
+ * at the given packet. Returns non-zero on success, 0 if an EOF was
+ * first encountered.
+ */
+
+static int
+read_up_to( pcap_t *p, struct timeval *desired_time )
+ {
+ struct pcap_pkthdr hdr;
+ const u_char *buf;
+ long pos;
+ int status;
+
+ for ( ; ; )
+ {
+ struct timeval *timestamp;
+
+ pos = ftell( pcap_file( p ) );
+ buf = pcap_next( p, &hdr );
+
+ if ( buf == 0 )
+ {
+ if ( feof( pcap_file( p ) ) )
+ {
+ status = 0;
+ clearerr( pcap_file( p ) );
+ break;
+ }
+
+ error( "bad status in read_up_to()" );
+ }
+
+ timestamp = &hdr.ts;
+
+ if ( ! sf_timestamp_less_than( timestamp, desired_time ) )
+ {
+ status = 1;
+ break;
+ }
+ }
+
+ if ( fseek( pcap_file( p ), pos, 0 ) < 0 )
+ error( "fseek() failed in read_up_to()" );
+
+ return (status);
+ }
+
+
+/* Positions the sf_readfile stream so that the next sf_read() will
+ * return the first packet with a time greater than or equal to
+ * desired_time. desired_time must be greater than min_time and less
+ * than max_time, which should correspond to actual packets in the
+ * file. min_pos is the file position (byte offset) corresponding to
+ * the min_time packet and max_pos is the same for the max_time packet.
+ *
+ * Returns non-zero on success, 0 if the given position is beyond max_pos.
+ *
+ * NOTE: when calling this routine, the sf_readfile stream *must* be
+ * already aligned so that the next call to sf_next_packet() will yield
+ * a valid packet.
+ */
+
+int
+sf_find_packet( pcap_t *p,
+ struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time )
+ {
+ int status = 1;
+ struct timeval min_time_copy, max_time_copy;
+ u_int num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
+ int num_bytes_read;
+ long desired_pos, present_pos;
+ u_char *buf, *hdrpos;
+ struct pcap_pkthdr hdr;
+
+ buf = (u_char *) malloc( num_bytes );
+ if ( ! buf )
+ error( "malloc() failured in sf_find_packet()" );
+
+ min_time_copy = *min_time;
+ min_time = &min_time_copy;
+
+ max_time_copy = *max_time;
+ max_time = &max_time_copy;
+
+ for ( ; ; ) /* loop until positioned correctly */
+ {
+ desired_pos =
+ interpolated_position( min_time, min_pos,
+ max_time, max_pos,
+ desired_time );
+
+ if ( desired_pos < 0 )
+ {
+ status = 0;
+ break;
+ }
+
+ present_pos = ftell( pcap_file( p ) );
+
+ if ( present_pos <= desired_pos &&
+ desired_pos - present_pos < STRAIGHT_SCAN_THRESHOLD )
+ { /* we're close enough to just blindly read ahead */
+ status = read_up_to( p, desired_time );
+ break;
+ }
+
+ /* Undershoot the target a little bit - it's much easier to
+ * then scan straight forward than to try to read backwards ...
+ */
+ desired_pos -= STRAIGHT_SCAN_THRESHOLD / 2;
+ if ( desired_pos < min_pos )
+ desired_pos = min_pos;
+
+ if ( fseek( pcap_file( p ), desired_pos, 0 ) < 0 )
+ error( "fseek() failed in sf_find_packet()" );
+
+ num_bytes_read =
+ fread( (char *) buf, 1, num_bytes, pcap_file( p ) );
+
+ if ( num_bytes_read == 0 )
+ /* This shouldn't ever happen because we try to
+ * undershoot, unless the dump file has only a
+ * couple packets in it ...
+ */
+ error( "fread() failed in sf_find_packet()" );
+
+ if ( find_header( p, buf, num_bytes, min_time->tv_sec,
+ max_time->tv_sec, &hdrpos, &hdr ) !=
+ HEADER_DEFINITELY )
+ error( "can't find header at position %ld in dump file",
+ desired_pos );
+
+ /* Correct desired_pos to reflect beginning of packet. */
+ desired_pos += (hdrpos - buf);
+
+ /* Seek to the beginning of the header. */
+ if ( fseek( pcap_file( p ), desired_pos, 0 ) < 0 )
+ error( "fseek() failed in sf_find_packet()" );
+
+ if ( sf_timestamp_less_than( &hdr.ts, desired_time ) )
+ { /* too early in the file */
+ *min_time = hdr.ts;
+ min_pos = desired_pos;
+ }
+
+ else if ( sf_timestamp_less_than( desired_time, &hdr.ts ) )
+ { /* too late in the file */
+ *max_time = hdr.ts;
+ max_pos = desired_pos;
+ }
+
+ else
+ /* got it! */
+ break;
+ }
+
+ free( (char *) buf );
+
+ return status;
+ }
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.1 b/usr.sbin/tcpdump/tcpslice/tcpslice.1
new file mode 100644
index 0000000..d804cf6
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.1
@@ -0,0 +1,273 @@
+.\" Copyright (c) 1988-1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that: (1) source code distributions
+.\" retain the above copyright notice and this paragraph in its entirety, (2)
+.\" distributions including binary code include the above copyright notice and
+.\" this paragraph in its entirety in the documentation or other materials
+.\" provided with the distribution, and (3) all advertising materials mentioning
+.\" features or use of this software display the following acknowledgement:
+.\" ``This product includes software developed by the University of California,
+.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+.\" the University nor the names 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 October 14, 1991
+.Dt TCPSLICE 1
+.Os
+.Sh NAME
+.Nm tcpslice
+.Nd extract pieces of and/or glue together tcpdump files
+.Sh SYNOPSIS
+.Nm
+.Op Fl dRrt
+.Op Fl w Ar file
+.Op Ar start-time Op end-time
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility extracts portions of packet-trace files generated using
+.Xr tcpdump 1 Ns 's
+.Fl w
+flag.
+It can also be used to glue together several such files, as discussed
+below.
+.Pp
+The basic operation of
+.Nm
+is to copy to
+.Pa stdout
+all packets from its input file(s) whose timestamps fall
+within a given range. The starting and ending times of the range
+may be specified on the command line. All ranges are inclusive.
+The starting time defaults
+to the time of the first packet in the first input file; we call
+this the
+.Em first time .
+The ending time defaults to ten years after the starting time.
+Thus, the command
+.Nm
+.Ar trace-file
+simply copies
+.Ar trace-file
+to
+.Pa stdout
+(assuming the file does not include more than
+ten years' worth of data).
+.Pp
+There are a number of ways to specify times. The first is using
+Unix timestamps of the form
+.Em sssssssss.uuuuuu
+(this is the format specified by
+.Xr tcpdump 1 Ns 's
+.Fl tt
+flag).
+For example,
+.Em 654321098.7654
+specifies 38 seconds and 765,400 microseconds
+after 8:51PM PDT, Sept. 25, 1990.
+.Pp
+All examples in this manual are given
+for PDT times, but when displaying times and interpreting times symbolically
+as discussed below,
+.Nm
+uses the local timezone, regardless of the timezone in which the
+.Xr tcpdump 1
+file was generated. The daylight-savings setting used is that which is
+appropriate for the local timezone at the date in question. For example,
+times associated with summer months will usually include daylight-savings
+effects, and those with winter months will not.
+.Pp
+Times may also be specified relative
+to either the
+.Em first time
+(when specifying a starting time)
+or the starting time (when specifying an ending time)
+by preceding a numeric value in seconds with a `+'.
+For example, a starting time of
+.Em +200
+indicates 200 seconds after the
+.Em first time ,
+and the two arguments
+.Em +200 +300
+indicate from 200 seconds after the
+.Em first time
+through 500 seconds after the
+.Em first time .
+.Pp
+Times may also be specified in terms of years (y), months (m), days (d),
+hours (h), minutes (m), seconds (s), and microseconds(u). For example,
+the Unix timestamp 654321098.7654 discussed above could also be expressed
+as
+.Em 90y9m25d20h51m38s765400u .
+.Pp
+When specifying times using this style, fields that are omitted default
+as follows. If the omitted field is a unit
+.Em greater
+than that of the first specified field, then its value defaults to
+the corresponding value taken from either
+.Em first time
+(if the starting time is being specified) or the starting time
+(if the ending time is being specified).
+If the omitted field is a unit
+.Em less
+than that of the first specified field, then it defaults to zero.
+For example, suppose that the input file has a
+.Em first time
+of the Unix timestamp mentioned above, i.e., 38 seconds and 765,400 microseconds
+after 8:51PM PDT, Sept. 25, 1990. To specify 9:36PM PDT (exactly) on the
+same date we could use
+.Em 21h36m .
+To specify a range from 9:36PM PDT through 1:54AM PDT the next day we
+could use
+.Em 21h36m 26d1h54m .
+.Pp
+Relative times can also be specified when using the
+.Em ymdhmsu
+format. Omitted fields then default to 0 if the unit of the field is
+.Em greater
+than that of the first specified field, and to the corresponding value
+taken from either the
+.Em first time
+or the starting time if the omitted field's unit is
+.Em less
+than that of the first specified field. Given a
+.Em first time
+of the Unix timestamp mentioned above,
+.Em 22h +1h10m
+specifies a range from 10:00PM PDT on that date through 11:10PM PDT, and
+.Em +1h +1h10m
+specifies a range from 38.7654 seconds after 9:51PM PDT through 38.7654
+seconds after 11:01PM PDT. The first hour of the file could be extracted
+using
+.Em +0 +1h .
+.Pp
+Note that with the
+.Em ymdhmsu
+format there is an ambiguity between using
+.Em m
+for `month' or for `minute'. The ambiguity is resolved as follows: if an
+.Em m
+field is followed by a
+.Em d
+field then it is interpreted as specifying months; otherwise it
+specifies minutes.
+.Pp
+If more than one input file is specified then
+.Nm
+first copies packets lying in the given range from the first file; it
+then increases the starting time of the range to lie just beyond the
+timestamp of the last packet in the first file, repeats the process
+with the second file, and so on. Thus files with interleaved packets
+are
+.Em not
+merged. For a given file, only packets that are newer than any in the
+preceding files will be considered. This mechanism avoids any possibility
+of a packet occurring more than once in the output.
+.Sh OPTIONS
+If any of
+.Fl R ,
+.Fl r
+or
+.Fl t
+are specified then
+.Nm
+reports the timestamps of the first and last packets in each input file
+and exits. Only one of these three options may be specified.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Dump the start and end times specified by the given range and
+exit. This option is useful for checking that the given range actually
+specifies the times you think it does. If one of
+.Fl R ,
+.Fl r
+or
+.Fl t
+has been specified then the times are dumped in the corresponding
+format; otherwise, raw format
+.Pq Fl R
+is used.
+.It Fl R
+Dump the timestamps of the first and last packets in each input file
+as raw timestamps (i.e., in the form
+.Em sssssssss.uuuuuu ) .
+.It Fl r
+Same as
+.Fl R
+except the timestamps are dumped in human-readable format, similar
+to that used by
+.Xr date 1 .
+.It Fl t
+Same as
+.Fl R
+except the timestamps are dumped in
+.Nm
+format, i.e., in the
+.Em ymdhmsu
+format discussed above.
+.It Fl w Ar file
+Direct the output to
+.Ar file
+rather than
+.Pa stdout .
+.El
+.Sh SEE ALSO
+.Xr tcpdump 1
+.Sh AUTHORS
+.An Vern Paxson Aq vern@ee.lbl.gov ,
+of Lawrence Berkeley Laboratory, University of California, Berkeley, CA.
+.Sh BUGS
+An input filename that beings with a digit or a `+' can be confused
+with a start/end time. Such filenames can be specified with a
+leading `./'; for example, specify the file `04Jul76.trace' as
+`./04Jul76.trace'.
+.Pp
+The
+.Nm
+utility cannot read its input from
+.Pa stdin ,
+since it uses random-access
+to rummage through its input files.
+.Pp
+The
+.Nm
+utility refuses to write to its output if it is a terminal
+(as indicated by
+.Xr isatty 3 ) .
+This is not a bug but a feature,
+to prevent it from spraying binary data to the user's terminal.
+Note that this means you must either redirect
+.Pa stdout
+or specify an
+output file via
+.Fl w .
+.Pp
+The
+.Nm
+utility will not work properly on
+.Xr tcpdump 1
+files spanning more than one year;
+with files containing portions of packets whose original length was
+more than 65,535 bytes; nor with files containing fewer than three packets.
+Such files result in
+the error message: `couldn't find final packet in file'. These problems
+are due to the interpolation scheme used by
+.Nm
+to greatly speed up its processing when dealing with large trace files.
+Note that
+.Nm
+can efficiently extract slices from the middle of trace files of any
+size, and can also work with truncated trace files (i.e., the final packet
+in the file is only partially present, typically due to
+.Xr tcpdump 1
+being ungracefully killed).
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.c b/usr.sbin/tcpdump/tcpslice/tcpslice.c
new file mode 100644
index 0000000..adca2f5
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 1987-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987-1990\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * tcpslice - extract pieces of and/or glue together tcpdump files
+ */
+
+#include <err.h>
+#include "tcpslice.h"
+
+int tflag = 0; /* global that util routines are sensitive to */
+int fddipad; /* XXX: libpcap needs this global */
+
+/* Style in which to print timestamps; RAW is "secs.usecs"; READABLE is
+ * ala the Unix "date" tool; and PARSEABLE is tcpslice's custom format,
+ * designed to be easy to parse. The default is RAW.
+ */
+enum stamp_styles { TIMESTAMP_RAW, TIMESTAMP_READABLE, TIMESTAMP_PARSEABLE };
+enum stamp_styles timestamp_style = TIMESTAMP_RAW;
+
+#ifndef __FreeBSD__
+extern int getopt( int argc, char **argv, char *optstring );
+#endif
+
+int is_timestamp( char *str );
+long local_time_zone(long timestamp);
+struct timeval parse_time(char *time_string, struct timeval base_time);
+void fill_tm(char *time_string, int is_delta, struct tm *t, time_t *usecs_addr);
+void get_file_range( char filename[], pcap_t **p,
+ struct timeval *first_time, struct timeval *last_time );
+struct timeval first_packet_time(char filename[], pcap_t **p_addr);
+void extract_slice(char filename[], char write_file_name[],
+ struct timeval *start_time, struct timeval *stop_time);
+char *timestamp_to_string(struct timeval *timestamp);
+void dump_times(pcap_t **p, char filename[]);
+static void usage(void);
+
+
+pcap_dumper_t *dumper = 0;
+
+int
+main(int argc, char **argv)
+{
+ int op;
+ int dump_flag = 0;
+ int report_times = 0;
+ char *start_time_string = 0;
+ char *stop_time_string = 0;
+ char *write_file_name = "-"; /* default is stdout */
+ struct timeval first_time, start_time, stop_time;
+ pcap_t *pcap;
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "dRrtw:")) != -1)
+ switch (op) {
+
+ case 'd':
+ dump_flag = 1;
+ break;
+
+ case 'R':
+ ++report_times;
+ timestamp_style = TIMESTAMP_RAW;
+ break;
+
+ case 'r':
+ ++report_times;
+ timestamp_style = TIMESTAMP_READABLE;
+ break;
+
+ case 't':
+ ++report_times;
+ timestamp_style = TIMESTAMP_PARSEABLE;
+ break;
+
+ case 'w':
+ write_file_name = optarg;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ if ( report_times > 1 )
+ error( "only one of -R, -r, or -t can be specified" );
+
+
+ if (optind < argc)
+ /* See if the next argument looks like a possible
+ * start time, and if so assume it is one.
+ */
+ if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
+ start_time_string = argv[optind++];
+
+ if (optind < argc)
+ if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
+ stop_time_string = argv[optind++];
+
+
+ if (optind >= argc)
+ error("at least one input file must be given");
+
+
+ first_time = first_packet_time(argv[optind], &pcap);
+ pcap_close(pcap);
+
+
+ if (start_time_string)
+ start_time = parse_time(start_time_string, first_time);
+ else
+ start_time = first_time;
+
+ if (stop_time_string)
+ stop_time = parse_time(stop_time_string, start_time);
+
+ else
+ {
+ stop_time = start_time;
+ stop_time.tv_sec += 86400*3660; /* + 10 years; "forever" */
+ }
+
+
+ if (report_times) {
+ for (; optind < argc; ++optind)
+ dump_times(&pcap, argv[optind]);
+ }
+
+ if (dump_flag) {
+ printf( "start\t%s\nstop\t%s\n",
+ timestamp_to_string( &start_time ),
+ timestamp_to_string( &stop_time ) );
+ }
+
+ if (! report_times && ! dump_flag) {
+ if ( ! strcmp( write_file_name, "-" ) &&
+ isatty( fileno(stdout) ) )
+ error("stdout is a terminal; redirect or use -w");
+
+ for (; optind < argc; ++optind)
+ extract_slice(argv[optind], write_file_name,
+ &start_time, &stop_time);
+ }
+
+ return 0;
+}
+
+
+/* Returns non-zero if a string matches the format for a timestamp,
+ * 0 otherwise.
+ */
+int is_timestamp( char *str )
+ {
+ while ( isdigit(*str) || *str == '.' )
+ ++str;
+
+ return *str == '\0';
+ }
+
+
+/* Return the correction in seconds for the local time zone with respect
+ * to Greenwich time.
+ */
+long local_time_zone(long timestamp)
+{
+ struct timeval now;
+ struct timezone tz;
+ long localzone;
+ time_t t = _long_to_time(timestamp);
+
+ if (gettimeofday(&now, &tz) < 0)
+ err(1, "gettimeofday");
+ localzone = tz.tz_minuteswest * -60;
+
+ if (localtime(&t)->tm_isdst)
+ localzone += 3600;
+
+ return localzone;
+}
+
+/* Given a string specifying a time (or a time offset) and a "base time"
+ * from which to compute offsets and fill in defaults, returns a timeval
+ * containing the specified time.
+ */
+
+struct timeval
+parse_time(char *time_string, struct timeval base_time)
+{
+ time_t tt = _long_to_time(base_time.tv_sec);
+ struct tm *bt = localtime(&tt);
+ struct tm t;
+ struct timeval result;
+ time_t usecs = 0;
+ int is_delta = (time_string[0] == '+');
+
+ if ( is_delta )
+ ++time_string; /* skip over '+' sign */
+
+ if ( is_timestamp( time_string ) )
+ { /* interpret as a raw timestamp or timestamp offset */
+ char *time_ptr;
+
+ result.tv_sec = atoi( time_string );
+ time_ptr = strchr( time_string, '.' );
+
+ if ( time_ptr )
+ { /* microseconds are specified, too */
+ int num_digits = strlen( time_ptr + 1 );
+ result.tv_usec = atoi( time_ptr + 1 );
+
+ /* turn 123.456 into 123 seconds plus 456000 usec */
+ while ( num_digits++ < 6 )
+ result.tv_usec *= 10;
+ }
+
+ else
+ result.tv_usec = 0;
+
+ if ( is_delta )
+ {
+ result.tv_sec += base_time.tv_sec;
+ result.tv_usec += base_time.tv_usec;
+
+ if ( result.tv_usec >= 1000000 )
+ {
+ result.tv_usec -= 1000000;
+ ++result.tv_sec;
+ }
+ }
+
+ return result;
+ }
+
+ if (is_delta) {
+ t = *bt;
+ usecs = base_time.tv_usec;
+ } else {
+ /* Zero struct (easy way around lack of tm_gmtoff/tm_zone
+ * under older systems) */
+ bzero((char *)&t, sizeof(t));
+
+ /* Set values to "not set" flag so we can later identify
+ * and default them.
+ */
+ t.tm_sec = t.tm_min = t.tm_hour = t.tm_mday = t.tm_mon =
+ t.tm_year = -1;
+ }
+
+ fill_tm(time_string, is_delta, &t, &usecs);
+
+ /* Now until we reach a field that was specified, fill in the
+ * missing fields from the base time.
+ */
+#define CHECK_FIELD(field_name) \
+ if (t.field_name < 0) \
+ t.field_name = bt->field_name; \
+ else \
+ break
+
+ do { /* bogus do-while loop so "break" in CHECK_FIELD will work */
+ CHECK_FIELD(tm_year);
+ CHECK_FIELD(tm_mon);
+ CHECK_FIELD(tm_mday);
+ CHECK_FIELD(tm_hour);
+ CHECK_FIELD(tm_min);
+ CHECK_FIELD(tm_sec);
+ } while ( 0 );
+
+ /* Set remaining unspecified fields to 0. */
+#define ZERO_FIELD_IF_NOT_SET(field_name,zero_val) \
+ if (t.field_name < 0) \
+ t.field_name = zero_val
+
+ if (! is_delta) {
+ ZERO_FIELD_IF_NOT_SET(tm_year,90); /* should never happen */
+ ZERO_FIELD_IF_NOT_SET(tm_mon,0);
+ ZERO_FIELD_IF_NOT_SET(tm_mday,1);
+ ZERO_FIELD_IF_NOT_SET(tm_hour,0);
+ ZERO_FIELD_IF_NOT_SET(tm_min,0);
+ ZERO_FIELD_IF_NOT_SET(tm_sec,0);
+ }
+
+ result.tv_sec = gwtm2secs(&t);
+ result.tv_sec -= local_time_zone(result.tv_sec);
+ result.tv_usec = usecs;
+
+ return result;
+}
+
+
+/* Fill in (or add to, if is_delta is true) the time values in the
+ * tm struct "t" as specified by the time specified in the string
+ * "time_string". "usecs_addr" is updated with the specified number
+ * of microseconds, if any.
+ */
+void
+fill_tm(char *time_string, int is_delta, struct tm *t, time_t *usecs_addr)
+{
+ char *t_start, *t_stop, format_ch;
+ int val;
+
+#define SET_VAL(lhs,rhs) \
+ if (is_delta) \
+ lhs += rhs; \
+ else \
+ lhs = rhs
+
+ /* Loop through the time string parsing one specification at
+ * a time. Each specification has the form <number><letter>
+ * where <number> indicates the amount of time and <letter>
+ * the units.
+ */
+ for (t_stop = t_start = time_string; *t_start; t_start = ++t_stop) {
+ if (! isdigit(*t_start))
+ error("bad date format %s, problem starting at %s",
+ time_string, t_start);
+
+ while (isdigit(*t_stop))
+ ++t_stop;
+ if (! t_stop)
+ error("bad date format %s, problem starting at %s",
+ time_string, t_start);
+
+ val = atoi(t_start);
+
+ format_ch = *t_stop;
+ if ( isupper( format_ch ) )
+ format_ch = tolower( format_ch );
+
+ switch (format_ch) {
+ case 'y':
+ if ( val >= 1900 )
+ val -= 1900;
+ else if (val < 100 && !is_delta) {
+ if (val < 69) /* Same hack as date */
+ val += 100;
+ }
+ SET_VAL(t->tm_year, val);
+ break;
+
+ case 'm':
+ if (strchr(t_stop+1, 'D') ||
+ strchr(t_stop+1, 'd'))
+ /* it's months */
+ SET_VAL(t->tm_mon, val - 1);
+ else /* it's minutes */
+ SET_VAL(t->tm_min, val);
+ break;
+
+ case 'd':
+ SET_VAL(t->tm_mday, val);
+ break;
+
+ case 'h':
+ SET_VAL(t->tm_hour, val);
+ break;
+
+ case 's':
+ SET_VAL(t->tm_sec, val);
+ break;
+
+ case 'u':
+ SET_VAL(*usecs_addr, val);
+ break;
+
+ default:
+ error(
+ "bad date format %s, problem starting at %s",
+ time_string, t_start);
+ }
+ }
+}
+
+
+/* Return in first_time and last_time the timestamps of the first and
+ * last packets in the given file.
+ */
+void
+get_file_range( char filename[], pcap_t **p,
+ struct timeval *first_time, struct timeval *last_time )
+{
+ *first_time = first_packet_time( filename, p );
+
+ if ( ! sf_find_end( *p, first_time, last_time ) )
+ error( "couldn't find final packet in file %s", filename );
+}
+
+int snaplen;
+
+/* Returns the timestamp of the first packet in the given tcpdump save
+ * file, which as a side-effect is initialized for further save-file
+ * reading.
+ */
+
+struct timeval
+first_packet_time(char filename[], pcap_t **p_addr)
+{
+ struct pcap_pkthdr hdr;
+ pcap_t *p;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ p = *p_addr = pcap_open_offline(filename, errbuf);
+ if (! p)
+ error( "bad tcpdump file %s: %s", filename, errbuf );
+
+ snaplen = pcap_snapshot( p );
+
+ if (pcap_next(p, &hdr) == 0)
+ error( "bad status reading first packet in %s", filename );
+
+ return hdr.ts;
+}
+
+
+/* Extract from the given file all packets with timestamps between
+ * the two time values given (inclusive). These packets are written
+ * to the save file given by write_file_name.
+ *
+ * Upon return, start_time is adjusted to reflect a time just after
+ * that of the last packet written to the output.
+ */
+
+void
+extract_slice(char filename[], char write_file_name[],
+ struct timeval *start_time, struct timeval *stop_time)
+{
+ long start_pos, stop_pos;
+ struct timeval file_start_time, file_stop_time;
+ struct pcap_pkthdr hdr;
+ pcap_t *p;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ p = pcap_open_offline(filename, errbuf);
+ if (! p)
+ error( "bad tcpdump file %s: %s", filename, errbuf );
+
+ snaplen = pcap_snapshot( p );
+ start_pos = ftell( pcap_file( p ) );
+
+ if ( ! dumper )
+ {
+ dumper = pcap_dump_open(p, write_file_name);
+ if ( ! dumper )
+ error( "error creating output file %s: ",
+ write_file_name, pcap_geterr( p ) );
+ }
+
+ if (pcap_next(p, &hdr) == 0)
+ error( "error reading packet in %s: ",
+ filename, pcap_geterr( p ) );
+
+ file_start_time = hdr.ts;
+
+
+ if ( ! sf_find_end( p, &file_start_time, &file_stop_time ) )
+ error( "problems finding end packet of file %s",
+ filename );
+
+ stop_pos = ftell( pcap_file( p ) );
+
+
+ /* sf_find_packet() requires that the time it's passed as its last
+ * argument be in the range [min_time, max_time], so we enforce
+ * that constraint here.
+ */
+ if ( sf_timestamp_less_than( start_time, &file_start_time ) )
+ *start_time = file_start_time;
+
+ if ( sf_timestamp_less_than( &file_stop_time, start_time ) )
+ return; /* there aren't any packets of interest in the file */
+
+
+ sf_find_packet( p, &file_start_time, start_pos,
+ &file_stop_time, stop_pos,
+ start_time );
+
+ for ( ; ; )
+ {
+ struct timeval *timestamp;
+ const u_char *pkt = pcap_next( p, &hdr );
+
+ if ( pkt == 0 )
+ {
+#ifdef notdef
+ int status;
+ if ( status != SFERR_EOF )
+ error( "bad status %d reading packet in %s",
+ status, filename );
+#endif
+ break;
+ }
+
+ timestamp = &hdr.ts;
+
+ if ( ! sf_timestamp_less_than( timestamp, start_time ) )
+ { /* packet is recent enough */
+ if ( sf_timestamp_less_than( stop_time, timestamp ) )
+ /* We've gone beyond the end of the region
+ * of interest ... We're done with this file.
+ */
+ break;
+
+ pcap_dump((u_char *) dumper, &hdr, pkt);
+
+ *start_time = *timestamp;
+
+ /* We know that each packet is guaranteed to have
+ * a unique timestamp, so we push forward the
+ * allowed minimum time to weed out duplicate
+ * packets.
+ */
+ ++start_time->tv_usec;
+ }
+ }
+
+ pcap_close( p );
+}
+
+
+/* Translates a timestamp to the time format specified by the user.
+ * Returns a pointer to the translation residing in a static buffer.
+ * There are two such buffers, which are alternated on subseqeuent
+ * calls, so two calls may be made to this routine without worrying
+ * about the results of the first call being overwritten by the
+ * results of the second.
+ */
+
+char *
+timestamp_to_string(struct timeval *timestamp)
+{
+ struct tm *t;
+ time_t tt;
+#define NUM_BUFFERS 2
+ static char buffers[NUM_BUFFERS][128];
+ static int buffer_to_use = 0;
+ char *buf;
+
+ buf = buffers[buffer_to_use];
+ buffer_to_use = (buffer_to_use + 1) % NUM_BUFFERS;
+
+ switch ( timestamp_style )
+ {
+ case TIMESTAMP_RAW:
+ sprintf(buf, "%lu.%06lu", timestamp->tv_sec, timestamp->tv_usec);
+ break;
+
+ case TIMESTAMP_READABLE:
+ tt = _long_to_time(timestamp->tv_sec);
+ t = localtime(&tt);
+ strcpy( buf, asctime( t ) );
+ buf[24] = '\0'; /* nuke final newline */
+ break;
+
+ case TIMESTAMP_PARSEABLE:
+ tt = _long_to_time(timestamp->tv_sec);
+ t = localtime(&tt);
+ if (t->tm_year >= 100)
+ t->tm_year += 1900;
+ sprintf( buf, "%02dy%02dm%02dd%02dh%02dm%02ds%06ldu",
+ t->tm_year, t->tm_mon + 1, t->tm_mday, t->tm_hour,
+ t->tm_min, t->tm_sec, timestamp->tv_usec );
+ break;
+
+ }
+
+ return buf;
+}
+
+
+/* Given a tcpdump save filename, reports on the times of the first
+ * and last packets in the file.
+ */
+
+void
+dump_times(pcap_t **p, char filename[])
+{
+ struct timeval first_time, last_time;
+
+ get_file_range( filename, p, &first_time, &last_time );
+
+ printf( "%s\t%s\t%s\n",
+ filename,
+ timestamp_to_string( &first_time ),
+ timestamp_to_string( &last_time ) );
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "tcpslice for tcpdump version %d.%d\n",
+ VERSION_MAJOR, VERSION_MINOR);
+ (void)fprintf(stderr,
+"usage: tcpslice [-dRrt] [-w file] [start-time [end-time]] file ... \n");
+
+ exit(1);
+}
+
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.h b/usr.sbin/tcpdump/tcpslice/tcpslice.h
new file mode 100644
index 0000000..14c69a7
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1987-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names 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.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <net/bpf.h>
+
+#include <ctype.h>
+#ifdef SOLARIS
+#include <fcntl.h>
+#endif
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+#include "pcap.h"
+#include "version.h"
+
+
+time_t gwtm2secs( struct tm *tm );
+
+int sf_find_end( struct pcap *p, struct timeval *first_timestamp,
+ struct timeval *last_timestamp );
+int sf_timestamp_less_than( struct timeval *t1, struct timeval *t2 );
+int sf_find_packet( struct pcap *p,
+ struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time );
+
+void error(const char *fmt, ...);
diff --git a/usr.sbin/tcpdump/tcpslice/util.c b/usr.sbin/tcpdump/tcpslice/util.c
new file mode 100644
index 0000000..8904352
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/util.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1988-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names 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.
+ */
+
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#) $FreeBSD$ (LBL)";
+#endif
+
+#include "tcpslice.h"
+
+/* VARARGS */
+void
+#if __STDC__
+error(const char *fmt, ...)
+#else
+error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "tcpslice: ");
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ exit(1);
+ /* NOTREACHED */
+}
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..6f73824
--- /dev/null
+++ b/usr.sbin/timed/timed/Makefile
@@ -0,0 +1,15 @@
+# @(#)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
+
+.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..c7204bd
--- /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 __P((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..09dfaaa
--- /dev/null
+++ b/usr.sbin/timed/timed/extern.h
@@ -0,0 +1,89 @@
+/*-
+ * 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 __P((char *, struct sockaddr_in *, struct netinfo *));
+struct hosttbl *findhost __P((char *));
+struct hosttbl *remmach __P((struct hosttbl *));
+
+struct tsp *readmsg __P((int,
+ char *, struct timeval *, struct netinfo *));
+struct tsp *acksend __P((struct tsp *,
+ struct sockaddr_in *, char *, int, struct netinfo *, int));
+
+void addnetname __P((char *));
+void adj_msg_time __P((struct tsp *, struct timeval *));
+void bytehostorder __P((struct tsp *));
+void bytenetorder __P((struct tsp *));
+void byteorder __P((struct tsp *));
+long casual __P((long, long));
+int cksum __P((u_short *, int));
+void correct __P((long));
+char *date __P((void));
+void doquit __P((struct tsp *));
+int election __P((struct netinfo *));
+void get_goodgroup __P((int));
+int good_host_name __P((char *));
+void ignoreack __P((void));
+int in_cksum __P((u_short *, int));
+void lookformaster __P((struct netinfo *));
+void makeslave __P((struct netinfo *));
+int master __P((void));
+void masterack __P((void));
+void masterup __P((struct netinfo *));
+int measure __P((u_long, u_long, char *, struct sockaddr_in *, int));
+void msterup __P((struct netinfo *));
+void mstotvround __P((struct timeval *, long));
+long networkdelta __P((void));
+void newslave __P((struct tsp *));
+void print __P((struct tsp *, struct sockaddr_in *));
+void prthp __P((clock_t));
+void rmnetmachs __P((struct netinfo *));
+void setstatus __P((void));
+int slave __P((void));
+void slaveack __P((void));
+void spreadtime __P((void));
+void suppress __P((struct sockaddr_in *, char *, struct netinfo *));
+void synch __P((long));
+void timevaladd __P((struct timeval *, struct timeval *));
+void timevalsub __P((struct timeval *, struct timeval *, struct timeval *));
+void traceoff __P((char *));
+void traceon __P((void));
+void xmit __P((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..6fe0bd1
--- /dev/null
+++ b/usr.sbin/timed/timed/globals.h
@@ -0,0 +1,175 @@
+/*-
+ * 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 <syslog.h>
+#include <unistd.h>
+
+#include <protocols/timed.h>
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+
+extern int sock;
+
+/* 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..0713155
--- /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 __P((struct tsp *));
+
+extern void logwtmp __P((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..cbbde58
--- /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 __P((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(a, eps_ptr, x, xlim, 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..db4d2dd
--- /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 __P((struct tsp *, char *));
+static void setmaster __P((struct tsp *));
+static void answerdelay __P((void));
+
+extern void logwtmp __P((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..e368764
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.8
@@ -0,0 +1,274 @@
+.\" 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 June 6, 1993
+.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 ...
+Create a list of trusted hosts.
+The
+.Nm
+utility 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.
+This option implies the
+.Fl M
+option.
+If this option is not specified,
+all hosts on the connected networks are treated as trustworthy.
+.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..27344a3
--- /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;
+ char 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..5262f46
--- /dev/null
+++ b/usr.sbin/timed/timedc/Makefile
@@ -0,0 +1,13 @@
+# @(#)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
+
+.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..7f33362
--- /dev/null
+++ b/usr.sbin/timed/timedc/extern.h
@@ -0,0 +1,52 @@
+/*-
+ * 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 __P((struct tsp *));
+void bytenetorder __P((struct tsp *));
+void clockdiff __P((int, char *[]));
+void help __P((int, char *[]));
+void intr __P((int));
+void makeargv __P((void));
+void msite __P((int, char *[]));
+int priv_resources __P((void));
+void quit __P((void));
+void testing __P((int, char *[]));
+void tracing __P((int, char *[]));
diff --git a/usr.sbin/timed/timedc/timedc.8 b/usr.sbin/timed/timedc/timedc.8
new file mode 100644
index 0000000..7c43ad4
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.8
@@ -0,0 +1,144 @@
+.\" 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 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 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 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..368b460
--- /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 __P((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..953c5d4
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile
@@ -0,0 +1,45 @@
+# $FreeBSD$
+
+TRACEROUTE_DISTDIR?= ${.CURDIR}/../../contrib/traceroute
+.PATH: ${TRACEROUTE_DISTDIR}
+
+PROG= traceroute
+MAN= traceroute.8
+SRCS= 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
+.ifndef (NOIPSEC)
+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
+
+.ifndef (NOIPSEC)
+DPADD= ${LIBIPSEC}
+LDADD= -lipsec
+.endif
+
+.if ${MACHINE_ARCH} == "alpha"
+# gcc builtin memcpy causes unaligned access
+CFLAGS+= -fno-builtin
+.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..ab4e52c
--- /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+= -DINET6 -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..99d8448
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.8
@@ -0,0 +1,162 @@
+.\" $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 dIlnrv
+.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
+.Nm
+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
+.Po in bytes
+.Pc
+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
+.It Fl m Ar hoplimit
+Specify maximum hoplimit, up to 255.
+The default is 30 hops.
+.Fl n
+is not specified, and only numeric addresses if
+.Fl n
+is specified.
+.It Fl n
+Do not resolve numeric address to hostname.
+.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
+.It Fl s Ar src
+.Ar Src
+specifies the source IPv6 address to be used.
+.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:
+.Pp
+.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 RETURN VALUES
+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..a854bde
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.c
@@ -0,0 +1,1369 @@
+/* $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 <netinet6/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 __P((int, char *[]));
+int wait_for_reply __P((int, struct msghdr *));
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int setpolicy __P((int so, char *policy));
+#endif
+#endif
+void send_probe __P((int, u_long));
+struct udphdr *get_udphdr __P((struct ip6_hdr *, u_char *));
+int get_hoplim __P((struct msghdr *));
+double deltaT __P((struct timeval *, struct timeval *));
+char *pr_type __P((int));
+int packet_ok __P((struct msghdr *, int, int));
+void print __P((struct msghdr *, int));
+const char *inetname __P((struct sockaddr *));
+void usage __P((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 useicmp;
+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;
+
+ /*
+ * Receive ICMP
+ */
+ if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ perror("socket(ICMPv6)");
+ exit(5);
+ }
+
+ /* revoke privs */
+ seteuid(getuid());
+ setuid(getuid());
+
+ 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:np:q:rs:w:v")) != -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':
+ useicmp++;
+ 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 '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 '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;
+
+ if (max_hops < first_hop) {
+ fprintf(stderr,
+ "traceroute6: max hoplimit must be larger than first hoplimit.\n");
+ 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);
+ }
+ }
+ if (useicmp)
+ minlen = ICMP6ECHOLEN + sizeof(struct tv32);
+ else
+ minlen = sizeof(struct opacket);
+ 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*/
+
+ /*
+ * Send UDP or ICMP
+ */
+ if (useicmp) {
+ sndsock = rcvsock;
+ } else {
+ if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket(SOCK_DGRAM)");
+ exit(5);
+ }
+ }
+#ifdef SO_SNDBUF
+ i = datalen;
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&i,
+ sizeof(i)) < 0) {
+ 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)) {
+ 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 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);
+
+ if (useicmp) {
+ struct icmp6_hdr *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));
+ } else {
+ struct opacket *op = outpacket;
+
+ op->seq = seq;
+ op->hops = hops;
+ bcopy(&tv32, &op->tv, sizeof tv32);
+ }
+
+ 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;
+ struct udphdr *up;
+
+ hip = (struct ip6_hdr *)(icp + 1);
+ if ((up = get_udphdr(hip, (u_char *)(buf + cc))) == NULL) {
+ if (verbose)
+ warnx("failed to get upper layer header");
+ return(0);
+ }
+ if (useicmp &&
+ ((struct icmp6_hdr *)up)->icmp6_id == ident &&
+ ((struct icmp6_hdr *)up)->icmp6_seq == htons(seq))
+ return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
+ else if (!useicmp &&
+ up->uh_sport == htons(srcport) &&
+ up->uh_dport == htons(port + seq))
+ return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
+ } else if (useicmp && 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.
+ */
+struct udphdr *
+get_udphdr(ip6, lim)
+ struct ip6_hdr *ip6;
+ u_char *lim;
+{
+ u_char *cp = (u_char *)ip6, nh;
+ int hlen;
+
+ if (cp + sizeof(*ip6) >= lim)
+ return(NULL);
+
+ nh = ip6->ip6_nxt;
+ cp += sizeof(struct ip6_hdr);
+
+ while (lim - cp >= 8) {
+ switch (nh) {
+ case IPPROTO_ESP:
+ case IPPROTO_TCP:
+ return(NULL);
+ case IPPROTO_ICMPV6:
+ return(useicmp ? (struct udphdr *)cp : NULL);
+ case IPPROTO_UDP:
+ return(useicmp ? NULL : (struct udphdr *)cp);
+ 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 [-dIlnrv] [-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..cf1680b
--- /dev/null
+++ b/usr.sbin/trpt/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= trpt
+MAN= trpt.8
+BINGRP= kmem
+BINMODE= 2555
+
+WARNS?= 4
+CFLAGS+= -DINET6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/trpt/trpt.8 b/usr.sbin/trpt/trpt.8
new file mode 100644
index 0000000..226ca4a
--- /dev/null
+++ b/usr.sbin/trpt/trpt.8
@@ -0,0 +1,153 @@
+.\" 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.
+.\"
+.\" @(#)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 SEE ALSO
+.Xr netstat 1 ,
+.Xr setsockopt 2
+.Sh DIAGNOSTICS
+.Bl -diag
+.It no namelist
+When the system image doesn't
+contain the proper symbols to find the trace buffer;
+others which should be self explanatory.
+.El
+.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.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/trpt/trpt.c b/usr.sbin/trpt/trpt.c
new file mode 100644
index 0000000..105a2bd
--- /dev/null
+++ b/usr.sbin/trpt/trpt.c
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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(act, ostate, tp, family, ip, th, req)
+ short act, ostate;
+ struct tcpcb *tp;
+ int family;
+ 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/paths.h b/usr.sbin/tzsetup/paths.h
new file mode 100644
index 0000000..4754e9a
--- /dev/null
+++ b/usr.sbin/tzsetup/paths.h
@@ -0,0 +1,5 @@
+#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"
diff --git a/usr.sbin/tzsetup/tzsetup.8 b/usr.sbin/tzsetup/tzsetup.8
new file mode 100644
index 0000000..642be78
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.8
@@ -0,0 +1,130 @@
+.\" 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 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.
+.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.
diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c
new file mode 100644
index 0000000..86a9ffb
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -0,0 +1,710 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#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>
+
+#include "paths.h"
+
+static int reallydoit = 1;
+
+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 {
+ 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 dialogMenuItem continents[] = {
+ { "1", "Africa", 0, continent_country_menu, 0, &africa },
+ { "2", "America -- North and South", 0, continent_country_menu, 0,
+ &america },
+ { "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
+ { "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
+ { "5", "Asia", 0, continent_country_menu, 0, &asia },
+ { "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
+ { "7", "Australia", 0, continent_country_menu, 0, &australia },
+ { "8", "Europe", 0, continent_country_menu, 0, &europe },
+ { "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
+ { "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
+};
+#define NCONTINENTS (int)((sizeof continents)/(sizeof continents[0]))
+#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
+
+static int
+continent_country_menu(dialogMenuItem *continent)
+{
+ int rv;
+ struct continent *contp = continent->data;
+ char title[256];
+ int isocean = OCEANP(continent - continents);
+ int menulen;
+
+ /* Short cut -- if there's only one country, don't post a menu. */
+ if (contp->nitems == 1) {
+ return set_zone_menu(&contp->menu[0]);
+ }
+
+ /* It's amazing how much good grammar really matters... */
+ if (!isocean)
+ snprintf(title, sizeof title, "Countries in %s",
+ continent->title);
+ else
+ snprintf(title, sizeof title, "Islands and groups in the %s",
+ continent->title);
+
+ menulen = contp->nitems < 16 ? contp->nitems : 16;
+ rv = dialog_menu(title, (isocean ? "Select an island or group"
+ : "Select a country"), -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;
+ char *s, *t, *name;
+ size_t len;
+ int lineno;
+ struct country *cp;
+
+ 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(%lu)", (unsigned long)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)
+{
+ FILE *fp;
+ char *line;
+ size_t len;
+ int lineno;
+ char *tlc, *coord, *file, *descr, *p;
+ char contbuf[16];
+ struct continent *cont;
+
+ 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 && *line) ? line : 0;
+
+ 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. We set
+ * nitems back to zero so that we can use it for counting
+ * again when we actually build the menus.
+ */
+ 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;
+ }
+
+ /*
+ * 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)
+{
+ int rv;
+ char buf[256];
+ struct country *cp = dmi->data;
+ int menulen;
+
+ snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
+ menulen = cp->nzones < 16 ? cp->nzones : 16;
+ rv = dialog_menu(buf, "Select a zone which observes the same time as "
+ "your locality.", -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)
+{
+ struct stat sb;
+ int fd1, fd2;
+ int copymode;
+ char *msg;
+ ssize_t len;
+ char buf[1024];
+
+ 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)
+ asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
+ else
+ asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
+ " to %s", filename);
+
+ dialog_notify(msg);
+ free(msg);
+#endif
+
+ if (reallydoit) {
+ if (copymode) {
+ fd1 = open(filename, O_RDONLY, 0);
+ if (fd1 < 0) {
+ asprintf(&msg, "Could not open %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ 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) {
+ asprintf(&msg, "Could not open "
+ _PATH_LOCALTIME ": %s",
+ strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+
+ while ((len = read(fd1, buf, sizeof buf)) > 0)
+ len = write(fd2, buf, len);
+
+ if (len == -1) {
+ asprintf(&msg, "Error copying %s to "
+ _PATH_LOCALTIME ": %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ /* 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) {
+ asprintf(&msg, "Cannot access %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ unlink(_PATH_LOCALTIME);
+ if (symlink(filename, _PATH_LOCALTIME) < 0) {
+ asprintf(&msg, "Cannot create symbolic link "
+ _PATH_LOCALTIME " to %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ }
+ }
+
+#ifdef VERBOSE
+ if (copymode)
+ asprintf(&msg, "Copied timezone file from %s to "
+ _PATH_LOCALTIME, filename);
+ else
+ asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
+ " to %s", filename);
+
+ dialog_mesgbox("Done", msg, 8, 72);
+ free(msg);
+#endif
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+confirm_zone(const char *filename)
+{
+ char *msg;
+ struct tm *tm;
+ time_t t = time(0);
+ int rv;
+
+ setenv("TZ", filename, 1);
+ tzset();
+ tm = localtime(&t);
+
+ asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
+ tm->tm_zone);
+ rv = !dialog_yesno("Confirmation", msg, 5, 72);
+ free(msg);
+ return rv;
+}
+
+static int
+set_zone_multi(dialogMenuItem *dmi)
+{
+ char *fn;
+ struct zone *zp = dmi->data;
+ 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)
+{
+ char *fn;
+ struct country *cp = dmi->data;
+ 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()
+{
+ fprintf(stderr, "usage: tzsetup [-n]\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, fd;
+ int (*dialog_utc)(unsigned char *, unsigned char *, int, int);
+
+#if defined(__alpha__) || defined(__sparc64__)
+ dialog_utc = dialog_yesno;
+#else
+ dialog_utc = dialog_noyes;
+#endif
+
+ 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();
+
+ init_dialog();
+ if (!dialog_utc("Select local or UTC (Greenwich Mean Time) clock",
+ "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!", 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) {
+ char *msg;
+ asprintf(&msg, "\nUse the default `%s' zone?", argv[optind]);
+ if (!dialog_yesno("Default timezone provided", msg, 7, 72)) {
+ install_zone_file(argv[optind]);
+ dialog_clear();
+ end_dialog();
+ return 0;
+ }
+ free(msg);
+ dialog_clear_norefresh();
+ }
+ dialog_menu("Time Zone Selector", "Select a region", -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..0d97b01
--- /dev/null
+++ b/usr.sbin/ugidfw/ugidfw.8
@@ -0,0 +1,210 @@
+.\" 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
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Cm object
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Cm mode
+.Ar arswxn
+.Nm
+.Cm list
+.Nm
+.Cm set
+.Ar rulenum
+.Cm subject
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Cm object
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Cm mode
+.Ar arswxn
+.Nm
+.Cm remove
+.Ar rulenum
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an
+.Xr ipfw 8 Ns -like
+interface to manage accesses 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 Cm add
+Add a new
+.Nm
+rule.
+.It Xo
+.Cm add
+.Cm subject
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Cm object
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.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
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Cm object
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.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
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Xc
+Subjects performing an operation must match
+(or, if
+.Cm not
+is specified, must
+.Em not
+match)
+the user and group specified by
+.Ar uid
+and/or
+.Ar gid
+for the rule to be applied.
+.It Xo
+.Cm object
+.Op Cm not
+.Op Cm uid Ar uid
+.Op Cm gid Ar gid
+.Xc
+Objects must be owned by
+(or, if
+.Cm not
+is specified, must
+.Em not
+be owned by)
+the user and/or group specified by
+.Ar uid
+and/or
+.Ar gid
+for the rule to be applied.
+.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..87ed035
--- /dev/null
+++ b/usr.sbin/ugidfw/ugidfw.c
@@ -0,0 +1,208 @@
+/*-
+ * 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/param.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+
+#include <security/mac_bsdextended/mac_bsdextended.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ugidfw.h>
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "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];
+ struct mac_bsdextended_rule rule;
+ long value;
+ int error, rulenum;
+ char *endp;
+
+ error = bsde_parse_rule(argc, argv, &rule, BUFSIZ, errstr);
+ if (error) {
+ fprintf(stderr, "%s\n", errstr);
+ return;
+ }
+
+ error = bsde_add_rule(&rulenum, &rule, BUFSIZ, errstr);
+ if (error) {
+ fprintf(stderr, "%s\n", errstr);
+ return;
+ }
+ printf("Added rule %d\n", rulenum);
+}
+
+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) {
+ fprintf(stderr, "Unable to get rule slots; mac_bsdextended.ko "
+ "may not be loaded.\n");
+ fprintf(stderr, "bsde_get_rule_slots: %s\n", errstr);
+ exit (-1);
+ }
+
+ rule_count = bsde_get_rule_count(BUFSIZ, errstr);
+ if (rule_count == -1) {
+ fprintf(stderr, "bsde_get_rule_count: %s\n", errstr);
+ exit (-1);
+ }
+
+ 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:
+ fprintf(stderr, "rule %d: %s\n", i, errstr);
+ continue;
+ case 0:
+ break;
+ }
+
+ if (bsde_rule_to_string(&rule, charstr, BUFSIZ) == -1)
+ fprintf(stderr,
+ "Unable to translate rule %d to string\n", 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) {
+ fprintf(stderr, "%s\n", errstr);
+ return;
+ }
+
+ error = bsde_set_rule(rulenum, &rule, BUFSIZ, errstr);
+ if (error) {
+ fprintf(stderr, "%s\n", 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)
+ fprintf(stderr, "%s\n", 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/usbd/Makefile b/usr.sbin/usbd/Makefile
new file mode 100644
index 0000000..aadecee
--- /dev/null
+++ b/usr.sbin/usbd/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= usbd
+MAN= usbd.conf.5 usbd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/usbd/usbd.8 b/usr.sbin/usbd/usbd.8
new file mode 100644
index 0000000..65e7ba3
--- /dev/null
+++ b/usr.sbin/usbd/usbd.8
@@ -0,0 +1,152 @@
+.\" $NetBSD: usbd.8,v 1.2 1998/07/13 11:01:50 augustss Exp $
+.\" Copyright (c) 1998 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 July 12, 1998
+.Dt USBD 8
+.Os
+.Sh NAME
+.Nm usbd
+.Nd supervise USB attach/detach
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar configfile
+.Op Fl d
+.Op Fl e
+.Op Fl f Ar device
+.Op Fl n
+.Op Fl t Ar timeout
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility handles USB device attachment and detachment.
+It does two things.
+Through opening the
+.Pa /dev/usb0 ,
+.Pa /dev/usb1 ,
+etc. devices, it enables the kernel to handle change requests from
+attached hubs.
+This functionality will be removed when the kernel has
+kernel threads.
+The (multiple)
+.Fl f Ar device
+command line options specify which controllers it should handle.
+Normally this option is not needed.
+.Pp
+If the
+.Dq usb ,
+.Dq ohci
+and
+.Dq uhci
+modules are not loaded,
+.Nm
+will load them automatically.
+.Pp
+The second part is the handling of the attachment and detachment of USB
+devices.
+The device
+.Pa /dev/usb
+is opened and events are read from it.
+Whenever a device is attached or
+detached the list of actions read from
+.Pa /etc/usbd.conf
+is searched for a matching entry.
+If found, the corresponding action is
+executed.
+.Pp
+The command line options are as follows:
+.Bl -tag -width Ds
+.It Fl c Ar filename
+Name of configuration file.
+The default is
+.Pa /etc/usbd.conf .
+.It Fl d
+Enable debugging to the standard output,
+and do not disassociate from the controlling terminal.
+.It Fl e
+Do one device tree exploration, no event queue handling and then exit.
+.It Fl f Ar device
+Specify the pathname of a USB controller device file.
+The flag may be repeated to watch more than one USB controller.
+The default is
+.Pa /dev/usb0
+through
+.Pa /dev/usb3 .
+Do not specify the device
+.Pa /dev/usb
+here.
+It is used for events only.
+.It Fl n
+Do not handle the event queue on /dev/usb.
+.It Fl t Ar timeout
+Set the timeout interval (in seconds) before an exploration happens
+without being triggered by a connect or disconnect.
+A timeout of 0 means that there is no timeout. The default is 30.
+.It Fl v
+Be verbose.
+Repeating the flag makes
+.Nm
+more verbose.
+.El
+.Sh FILES
+.Bl -tag -width /etc/usbd.conf -compact
+.It Pa /etc/usbd.conf
+.It Pa /dev/usb
+.It Pa /dev/usb0
+.It Pa /dev/usb1
+.It etc .
+.El
+.Sh SEE ALSO
+.Xr usb 4 ,
+.Xr usbd.conf 5
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.4 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Lennart Augustsson Aq augustss@carlstedt.se
+for the
+.Nx
+project.
+The event queue handling in
+.Nm
+was added by
+.An Nick Hibma Aq n_hibma@FreeBSD.org .
diff --git a/usr.sbin/usbd/usbd.c b/usr.sbin/usbd/usbd.c
new file mode 100644
index 0000000..5cc2518
--- /dev/null
+++ b/usr.sbin/usbd/usbd.c
@@ -0,0 +1,1116 @@
+/* $NetBSD: usbd.c,v 1.4 1998/12/09 00:57:19 augustss 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.
+ */
+
+/* USBD creates 'threads' in the kernel, used for doing discovery when a
+ * device has attached or detached. This functionality should be removed
+ * once kernel threads have been added to the kernel.
+ * It also handles the event queue, and executing commands based on those
+ * events.
+ *
+ * See usbd(8).
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <signal.h>
+#include <paths.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <regex.h>
+
+#include <dev/usb/usb.h>
+
+/* default name of configuration file
+ */
+
+#define CONFIGFILE "/etc/usbd.conf"
+
+/* the name of the device spitting out usb attach/detach events as well as
+ * the prefix for the individual busses (used as a semi kernel thread).
+ */
+#define USBDEV "/dev/usb"
+
+/* Maximum number of USB busses expected to be in a system
+ * XXX should be replaced by dynamic allocation.
+ */
+#define MAXUSBDEV 4
+
+/* Sometimes a device does not respond in time for interrupt
+ * driven explore to find it. Therefore we run an exploration
+ * at regular intervals to catch those.
+ */
+#define TIMEOUT 30
+
+/* The wildcard used in actions for strings and integers
+ */
+#define WILDCARD_STRING NULL
+#define WILDCARD_INT -1
+
+
+extern char *__progname; /* name of program */
+
+char *configfile = CONFIGFILE; /* name of configuration file */
+
+char *devs[MAXUSBDEV]; /* device names */
+int fds[MAXUSBDEV]; /* file descriptors for USBDEV\d+ */
+int ndevs = 0; /* number of entries in fds / devs */
+int fd = -1; /* file descriptor for USBDEV */
+
+int lineno;
+int verbose = 0; /* print message on what it is doing */
+
+typedef struct event_name_s {
+ int type; /* event number (from usb.h) */
+ char *name;
+} event_name_t;
+
+event_name_t event_names[] = {
+ {USB_EVENT_CTRLR_ATTACH, "ctrlr-attach"},
+ {USB_EVENT_CTRLR_DETACH, "ctrlr-detach"},
+ {USB_EVENT_DRIVER_ATTACH, "driver-attach"},
+ {USB_EVENT_DRIVER_DETACH, "driver-detach"},
+ {USB_EVENT_DEVICE_ATTACH, "device-attach"},
+ {USB_EVENT_DEVICE_DETACH, "device-detach"},
+ {0, NULL} /* NULL indicates end of list, not 0 */
+};
+
+#define DEVICE_FIELD 0 /* descriptive field */
+
+#define VENDOR_FIELD 1 /* selective fields */
+#define PRODUCT_FIELD 2
+#define RELEASE_FIELD 3
+#define CLASS_FIELD 4
+#define SUBCLASS_FIELD 5
+#define PROTOCOL_FIELD 6
+#define DEVNAME_FIELD 7
+
+#define ATTACH_FIELD 8 /* command fields */
+#define DETACH_FIELD 9
+
+
+typedef struct action_s {
+ char *name; /* descriptive string */
+
+ int vendor; /* selection criteria */
+ int product;
+ int release;
+ int class;
+ int subclass;
+ int protocol;
+ char *devname;
+ regex_t devname_regex;
+
+ char *attach; /* commands to execute */
+ char *detach;
+
+ STAILQ_ENTRY(action_s) next;
+} action_t;
+
+STAILQ_HEAD(action_list, action_s) actions = STAILQ_HEAD_INITIALIZER(actions);
+
+typedef struct action_match_s {
+ action_t *action;
+ char *devname;
+} action_match_t;
+
+
+/* the function returns 0 for failure, 1 for all arguments found and 2 for
+ * arguments left over in trail.
+ */
+typedef int (*config_field_fn) __P((action_t *action, char *args,
+ char **trail));
+
+int set_device_field(action_t *action, char *args, char **trail);
+int set_vendor_field(action_t *action, char *args, char **trail);
+int set_product_field(action_t *action, char *args, char **trail);
+int set_release_field(action_t *action, char *args, char **trail);
+int set_class_field(action_t *action, char *args, char **trail);
+int set_subclass_field(action_t *action, char *args, char **trail);
+int set_protocol_field(action_t *action, char *args, char **trail);
+int set_devname_field(action_t *action, char *args, char **trail);
+int set_attach_field(action_t *action, char *args, char **trail);
+int set_detach_field(action_t *action, char *args, char **trail);
+
+/* the list of fields supported in an entry */
+typedef struct config_field_s {
+ int event;
+ char *name;
+ config_field_fn function;
+} config_field_t;
+
+config_field_t config_fields[] = {
+ {DEVICE_FIELD, "device", set_device_field},
+
+ {VENDOR_FIELD, "vendor", set_vendor_field},
+ {PRODUCT_FIELD, "product", set_product_field},
+ {RELEASE_FIELD, "release", set_release_field},
+ {CLASS_FIELD, "class", set_class_field},
+ {SUBCLASS_FIELD, "subclass", set_subclass_field},
+ {PROTOCOL_FIELD, "protocol", set_protocol_field},
+ {DEVNAME_FIELD, "devname", set_devname_field},
+
+ {ATTACH_FIELD, "attach", set_attach_field},
+ {DETACH_FIELD, "detach", set_detach_field},
+
+ {0, NULL, NULL} /* NULL is EOL marker, not the 0 */
+};
+
+
+/* prototypes for some functions */
+void print_event __P((struct usb_event *event));
+void print_action __P((action_t *action, int i));
+void print_actions __P((void));
+int find_action __P((struct usb_device_info *devinfo,
+ action_match_t *action_match));
+
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-d] [-v] [-t timeout] [-e] [-f dev]\n"
+ " [-n] [-c config]\n",
+ __progname);
+ exit(1);
+}
+
+
+/* generic helper functions for the functions to set the fields of actions */
+int
+get_string(char *src, char **rdst, char **rsrc)
+{
+ /* Takes the first string from src, taking quoting into account.
+ * rsrc (if not NULL) is set to the first byte not included in the
+ * string returned in rdst.
+ *
+ * Input is:
+ * src = 'fir"st \'par"t second part';
+ * Returned is:
+ * *dst = 'hello \'world';
+ * if (rsrc != NULL)
+ * *rsrc = 'second part';
+ *
+ * Notice the fact that the single quote enclosed in double quotes is
+ * returned. Also notice that before second part there is more than
+ * one space, which is removed in rsrc.
+ *
+ * The string in src is not modified.
+ */
+
+ char *dst; /* destination string */
+ int i; /* index into src */
+ int j; /* index into dst */
+ int quoted = 0; /* 1 for single, 2 for double quoted */
+
+ dst = malloc(strlen(src)+1); /* XXX allocation is too big, realloc?*/
+ if (dst == NULL) { /* should not happen, really */
+ fprintf(stderr, "%s:%d: Out of memory\n", configfile, lineno);
+ exit(2);
+ }
+
+ /* find the end of the current string. If quotes are found the search
+ * continues until the corresponding quote is found.
+ * So,
+ * hel'lo" "wor'ld
+ * represents the string
+ * hello" "world
+ * and not (hello world).
+ */
+ for (i = 0, j = 0; i < strlen(src); i++) {
+ if (src[i] == '\'' && (quoted == 0 || quoted == 1)) {
+ quoted = (quoted? 0:1);
+ } else if (src[i] == '"' && (quoted == 0 || quoted == 2)) {
+ quoted = (quoted? 0:2);
+ } else if (isspace(src[i]) && !quoted) {
+ /* found a space outside quotes -> terminates src */
+ break;
+ } else {
+ dst[j++] = src[i]; /* copy character */
+ }
+ }
+
+ /* quotes being left open? */
+ if (quoted) {
+ fprintf(stderr, "%s:%d: Missing %s quote at end of '%s'\n",
+ configfile, lineno,
+ (quoted == 1? "single":"double"), src);
+ exit(2);
+ }
+
+ /* skip whitespace for second part */
+ for (/*i is set*/; i < strlen(src) && isspace(src[i]); i++)
+ ; /* nop */
+
+ dst[j] = '\0'; /* make sure it's NULL terminated */
+
+ *rdst = dst; /* and return the pointers */
+ if (rsrc != NULL) /* if info wanted */
+ *rsrc = &src[i];
+
+ if (*dst == '\0') { /* empty string */
+ return 0;
+ } else if (src[i] == '\0') { /* completely used (1 argument) */
+ return 1;
+ } else { /* 2 or more args, *rsrc is rest */
+ return 2;
+ }
+}
+
+int
+get_integer(char *src, int *dst, char **rsrc)
+{
+ char *endptr;
+
+ /* Converts str to a number. If one argument was found in
+ * str, 1 is returned and *dst is set to the value of the integer.
+ * If 2 or more arguments were presented, 2 is returned,
+ * *dst is set to the converted value and rsrc, if not null, points
+ * at the start of the next argument (whitespace skipped).
+ * Else 0 is returned and nothing else is valid.
+ */
+
+ if (src == NULL || *src == '\0') /* empty src */
+ return(0);
+
+ *dst = (int) strtol(src, &endptr, 0);
+
+ /* skip over whitespace of second argument */
+ while (isspace(*endptr))
+ endptr++;
+
+ if (rsrc)
+ *rsrc = endptr;
+
+ if (isspace(*endptr)) { /* partial match, 2 or more arguments */
+ return(2);
+ } else if (*endptr == '\0') { /* full match, 1 argument */
+ return(1);
+ } else { /* invalid src, no match */
+ return(0);
+ }
+}
+
+/* functions to set the fields of the actions appropriately */
+int
+set_device_field(action_t *action, char *args, char **trail)
+{
+ return(get_string(args, &action->name, trail));
+}
+int
+set_vendor_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->vendor, trail));
+}
+int
+set_product_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->product, trail));
+}
+int
+set_release_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->release, trail));
+}
+int
+set_class_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->class, trail));
+}
+int
+set_subclass_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->subclass, trail));
+}
+int
+set_protocol_field(action_t *action, char *args, char **trail)
+{
+ return(get_integer(args, &action->protocol, trail));
+}
+int
+set_devname_field(action_t *action, char *args, char **trail)
+{
+ int match = get_string(args, &action->devname, trail);
+ int len;
+ int error;
+ char *string;
+# define ERRSTR_SIZE 100
+ char errstr[ERRSTR_SIZE];
+
+ if (match == 0)
+ return(0);
+
+ len = strlen(action->devname);
+ string = malloc(len + 15);
+ if (string == NULL)
+ return(0);
+
+ bcopy(action->devname, string+7, len); /* make some space for */
+ bcopy("[[:<:]]", string, 7); /* beginning of word */
+ bcopy("[[:>:]]", string+7+len, 7); /* and end of word */
+ string[len + 14] = '\0';
+
+ error = regcomp(&action->devname_regex, string, REG_NOSUB|REG_EXTENDED);
+ if (error) {
+ errstr[0] = '\0';
+ regerror(error, &action->devname_regex, errstr, ERRSTR_SIZE);
+ fprintf(stderr, "%s:%d: %s\n", configfile, lineno, errstr);
+ return(0);
+ }
+
+ return(match);
+}
+int
+set_attach_field(action_t *action, char *args, char **trail)
+{
+ return(get_string(args, &action->attach, trail));
+}
+int
+set_detach_field(action_t *action, char *args, char **trail)
+{
+ return(get_string(args, &action->detach, trail));
+}
+
+
+void
+read_configuration(void)
+{
+ FILE *file; /* file descriptor */
+ char *line; /* current line */
+ char *linez; /* current line, NULL terminated */
+ char *field; /* first part, the field name */
+ char *args; /* second part, arguments */
+ char *trail; /* remaining part after parsing, should be '' */
+ size_t len; /* length of current line */
+ int i,j; /* loop counters */
+ action_t *action = NULL; /* current action */
+
+ file = fopen(configfile, "r");
+ if (file == NULL) {
+ fprintf(stderr, "%s: Could not open for reading, %s\n",
+ configfile, strerror(errno));
+ exit(2);
+ }
+
+ for (lineno = 1; /* nop */;lineno++) {
+
+ line = fgetln(file, &len);
+ if (line == NULL) {
+ if (feof(file)) /* EOF */
+ break;
+ if (ferror(file)) {
+ fprintf(stderr, "%s:%d: Could not read, %s\n",
+ configfile, lineno, strerror(errno));
+ exit(2);
+ }
+ }
+
+ /* skip initial spaces */
+ while (len > 0 && isspace(*line)) {
+ line++;
+ len--;
+ }
+
+ if (len == 0) /* empty line */
+ continue;
+ if (line[0] == '#') /* comment line */
+ continue;
+
+ /* make a NULL terminated copy of the string */
+ linez = malloc(len+1);
+ if (linez == NULL) {
+ fprintf(stderr, "%s:%d: Out of memory\n",
+ configfile, lineno);
+ exit(2);
+ }
+ strncpy(linez, line, len);
+ linez[len] = '\0';
+
+ /* find the end of the current word (is field), that's the
+ * start of the arguments
+ */
+ field = linez;
+ args = linez;
+ while (*args != '\0' && !isspace(*args))
+ args++;
+
+ /* If arguments is not the empty string, NULL terminate the
+ * field and move the argument pointer to the first character
+ * of the arguments.
+ * If arguments is the empty string field and arguments both
+ * are terminated (strlen(field) >= 0, strlen(arguments) == 0).
+ */
+ if (*args != '\0') {
+ *args = '\0';
+ args++;
+ }
+
+ /* Skip initial spaces */
+ while (*args != '\0' && isspace(*args))
+ args++;
+
+ /* Cut off trailing whitespace */
+ for (i = 0, j = 0; args[i] != '\0'; i++)
+ if (!isspace(args[i]))
+ j = i+1;
+ args[j] = '\0';
+
+ /* We now have the field and the argument separated into
+ * two strings that are NULL terminated
+ */
+
+ /* If the field is 'device' we have to start a new action. */
+ if (strcmp(field, "device") == 0) {
+ /* Allocate a new action and set defaults */
+ action = malloc(sizeof(*action));
+ if (action == NULL) {
+ fprintf(stderr, "%s:%d: Out of memory\n",
+ configfile, lineno);
+ exit(2);
+ }
+ memset(action, 0, sizeof(*action));
+ action->product = WILDCARD_INT;
+ action->vendor = WILDCARD_INT;
+ action->release = WILDCARD_INT;
+ action->class = WILDCARD_INT;
+ action->subclass = WILDCARD_INT;
+ action->protocol = WILDCARD_INT;
+ action->devname = WILDCARD_STRING;
+
+ /* Add it to the end of the list to preserve order */
+ STAILQ_INSERT_TAIL(&actions, action, next);
+ }
+
+ if (action == NULL) {
+ line[len] = '\0'; /* XXX zero terminate */
+ fprintf(stderr, "%s:%d: Doesn't start with 'device' "
+ "but '%s'\n", configfile, lineno, field);
+ exit(2);
+ }
+
+ for (i = 0; config_fields[i].name ; i++) {
+ /* does the field name match? */
+ if (strcmp(config_fields[i].name, field) == 0) {
+ /* execute corresponding set-field function */
+ if ((config_fields[i].function)(action, args,
+ &trail)
+ != 1) {
+ fprintf(stderr,"%s:%d: "
+ "Syntax error in '%s'\n",
+ configfile, lineno, linez);
+ exit(2);
+ }
+ break;
+ }
+ }
+ if (config_fields[i].name == NULL) { /* Reached end of list*/
+ fprintf(stderr, "%s:%d: Unknown field '%s'\n",
+ configfile, lineno, field);
+ exit(2);
+ }
+ }
+
+ fclose(file);
+
+ if (verbose >= 2)
+ print_actions();
+}
+
+
+void
+print_event(struct usb_event *event)
+{
+ int i;
+ struct timespec *timespec = &event->ue_time;
+ struct usb_device_info *devinfo = &event->u.ue_device;
+
+ printf("%s: ", __progname);
+ for (i = 0; event_names[i].name != NULL; i++) {
+ if (event->ue_type == event_names[i].type) {
+ printf("%s event", event_names[i].name);
+ break;
+ }
+ }
+ if (event_names[i].name == NULL)
+ printf("unknown event %d", event->ue_type);
+
+ if (event->ue_type == USB_EVENT_DEVICE_ATTACH ||
+ event->ue_type == USB_EVENT_DEVICE_DETACH) {
+ devinfo = &event->u.ue_device;
+
+ printf(" at %ld.%09ld, %s, %s:\n",
+ timespec->tv_sec, timespec->tv_nsec,
+ devinfo->udi_product, devinfo->udi_vendor);
+
+ printf(" vndr=0x%04x prdct=0x%04x rlse=0x%04x "
+ "clss=0x%04x subclss=0x%04x prtcl=0x%04x\n",
+ devinfo->udi_vendorNo, devinfo->udi_productNo,
+ devinfo->udi_releaseNo,
+ devinfo->udi_class, devinfo->udi_subclass, devinfo->udi_protocol);
+
+ if (devinfo->udi_devnames[0][0] != '\0') {
+ char c = ' ';
+
+ printf(" device names:");
+ for (i = 0; i < USB_MAX_DEVNAMES; i++) {
+ if (devinfo->udi_devnames[i][0] == '\0')
+ break;
+
+ printf("%c%s", c, devinfo->udi_devnames[i]);
+ c = ',';
+ }
+ }
+ } else if (event->ue_type == USB_EVENT_CTRLR_ATTACH ||
+ event->ue_type == USB_EVENT_CTRLR_DETACH) {
+ printf(" bus=%d", &event->u.ue_ctrlr.ue_bus);
+ } else if (event->ue_type == USB_EVENT_DRIVER_ATTACH ||
+ event->ue_type == USB_EVENT_DRIVER_DETACH) {
+ printf(" cookie=%u devname=%s",
+ &event->u.ue_driver.ue_cookie.cookie,
+ &event->u.ue_driver.ue_devname);
+ }
+ printf("\n");
+}
+
+void
+print_action(action_t *action, int i)
+{
+ if (action == NULL)
+ return;
+
+ printf("%s: action %d: %s\n",
+ __progname, i,
+ (action->name? action->name:""));
+ if (action->product != WILDCARD_INT ||
+ action->vendor != WILDCARD_INT ||
+ action->release != WILDCARD_INT ||
+ action->class != WILDCARD_INT ||
+ action->subclass != WILDCARD_INT ||
+ action->protocol != WILDCARD_INT)
+ printf(" ");
+ if (action->vendor != WILDCARD_INT)
+ printf(" vndr=0x%04x", action->vendor);
+ if (action->product != WILDCARD_INT)
+ printf(" prdct=0x%04x", action->product);
+ if (action->release != WILDCARD_INT)
+ printf(" rlse=0x%04x", action->release);
+ if (action->class != WILDCARD_INT)
+ printf(" clss=0x%04x", action->class);
+ if (action->subclass != WILDCARD_INT)
+ printf(" subclss=0x%04x", action->subclass);
+ if (action->protocol != WILDCARD_INT)
+ printf(" prtcl=0x%04x", action->protocol);
+ if (action->vendor != WILDCARD_INT ||
+ action->product != WILDCARD_INT ||
+ action->release != WILDCARD_INT ||
+ action->class != WILDCARD_INT ||
+ action->subclass != WILDCARD_INT ||
+ action->protocol != WILDCARD_INT)
+ printf("\n");
+ if (action->devname != WILDCARD_STRING)
+ printf(" devname: %s\n", action->devname);
+
+ if (action->attach != NULL)
+ printf(" attach='%s'\n",
+ action->attach);
+ if (action->detach != NULL)
+ printf(" detach='%s'\n",
+ action->detach);
+}
+
+void
+print_actions()
+{
+ int i = 0;
+ action_t *action;
+
+ STAILQ_FOREACH(action, &actions, next)
+ print_action(action, ++i);
+
+ printf("%s: %d action%s\n", __progname, i, (i == 1? "":"s"));
+}
+
+
+int
+match_devname(action_t *action, struct usb_device_info *devinfo)
+{
+ int i;
+ regmatch_t match;
+ int error;
+
+ for (i = 0; i < USB_MAX_DEVNAMES; i++) {
+ if (devinfo->udi_devnames[i][0] == '\0')
+ break;
+
+ error = regexec(&action->devname_regex, devinfo->udi_devnames[i],
+ 1, &match, 0);
+ if (error == 0) {
+ if (verbose >= 2)
+ printf("%s: %s matches %s\n", __progname,
+ devinfo->udi_devnames[i], action->devname);
+ return(i);
+ }
+ }
+
+ return(-1);
+}
+
+
+int
+find_action(struct usb_device_info *devinfo, action_match_t *action_match)
+{
+ action_t *action;
+ char *devname = NULL;
+ int match = -1;
+
+ STAILQ_FOREACH(action, &actions, next) {
+ if ((action->vendor == WILDCARD_INT ||
+ action->vendor == devinfo->udi_vendorNo) &&
+ (action->product == WILDCARD_INT ||
+ action->product == devinfo->udi_productNo) &&
+ (action->release == WILDCARD_INT ||
+ action->release == devinfo->udi_releaseNo) &&
+ (action->class == WILDCARD_INT ||
+ action->class == devinfo->udi_class) &&
+ (action->subclass == WILDCARD_INT ||
+ action->subclass == devinfo->udi_subclass) &&
+ (action->protocol == WILDCARD_INT ||
+ action->protocol == devinfo->udi_protocol) &&
+ (action->devname == WILDCARD_STRING ||
+ (match = match_devname(action, devinfo)) != -1)) {
+ /* found match !*/
+
+ /* Find a devname for pretty printing. Either
+ * the matched one or otherwise, if there is only
+ * one devname for that device, use that.
+ */
+ if (match >= 0)
+ devname = devinfo->udi_devnames[match];
+ else if (devinfo->udi_devnames[0][0] != '\0' &&
+ devinfo->udi_devnames[1][0] == '\0')
+ /* if we have exactly 1 device name */
+ devname = devinfo->udi_devnames[0];
+
+ if (verbose) {
+ printf("%s: Found action '%s' for %s, %s",
+ __progname, action->name,
+ devinfo->udi_product, devinfo->udi_vendor);
+ if (devname)
+ printf(" at %s", devname);
+ printf("\n");
+ }
+
+ action_match->action = action;
+ action_match->devname = devname;
+
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+void
+execute_command(char *cmd)
+{
+ pid_t pid;
+ struct sigaction ign, intact, quitact;
+ sigset_t newsigblock, oldsigblock;
+ int status;
+ int i;
+
+ if (verbose)
+ printf("%s: Executing '%s'\n", __progname, cmd);
+ if (cmd == NULL)
+ return;
+
+ /* The code below is directly taken from the system(3) call.
+ * Added to it is the closing of open file descriptors.
+ */
+ /*
+ * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
+ * existing signal dispositions.
+ */
+ ign.sa_handler = SIG_IGN;
+ (void) sigemptyset(&ign.sa_mask);
+ ign.sa_flags = 0;
+ (void) sigaction(SIGINT, &ign, &intact);
+ (void) sigaction(SIGQUIT, &ign, &quitact);
+ (void) sigemptyset(&newsigblock);
+ (void) sigaddset(&newsigblock, SIGCHLD);
+ (void) sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "%s: fork failed, %s\n",
+ __progname, strerror(errno));
+ } else if (pid == 0) {
+ /* child here */
+
+ /* close all open file handles for USBDEV\d* devices */
+ for (i = 0; i < ndevs; i++)
+ close(fds[i]); /* USBDEV\d+ */
+ close(fd); /* USBDEV */
+
+ /* Restore original signal dispositions and exec the command. */
+ (void) sigaction(SIGINT, &intact, NULL);
+ (void) sigaction(SIGQUIT, &quitact, NULL);
+ (void) sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+
+ execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
+
+ /* should only be reached in case of error */
+ exit(127);
+ } else {
+ /* parent here */
+ do {
+ pid = waitpid(pid, &status, 0);
+ } while (pid == -1 && errno == EINTR);
+ }
+ (void) sigaction(SIGINT, &intact, NULL);
+ (void) sigaction(SIGQUIT, &quitact, NULL);
+ (void) sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+
+ if (pid == -1) {
+ fprintf(stderr, "%s: waitpid returned: %s\n",
+ __progname, strerror(errno));
+ } else if (pid == 0) {
+ fprintf(stderr, "%s: waitpid returned 0 ?!\n",
+ __progname);
+ } else {
+ if (status == -1) {
+ fprintf(stderr, "%s: Could not start '%s'\n",
+ __progname, cmd);
+ } else if (status == 127) {
+ fprintf(stderr, "%s: Shell failed for '%s'\n",
+ __progname, cmd);
+ } else if (WIFEXITED(status) && WEXITSTATUS(status)) {
+ fprintf(stderr, "%s: '%s' returned %d\n",
+ __progname, cmd, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s: '%s' caught signal %d\n",
+ __progname, cmd, WTERMSIG(status));
+ } else if (verbose >= 2) {
+ printf("%s: '%s' is ok\n", __progname, cmd);
+ }
+ }
+}
+
+void
+process_event_queue(int fd)
+{
+ struct usb_event event;
+ int error;
+ int len;
+ action_match_t action_match;
+
+ for (;;) {
+ len = read(fd, &event, sizeof(event));
+ if (len == -1) {
+ if (errno == EWOULDBLOCK) {
+ /* no more events */
+ break;
+ } else {
+ fprintf(stderr,"%s: Could not read event, %s\n",
+ __progname, strerror(errno));
+ exit(1);
+ }
+ }
+ if (len == 0)
+ break;
+ if (len != sizeof(event)) {
+ fprintf(stderr, "partial read on %s\n", USBDEV);
+ exit(1);
+ }
+
+ /* we seem to have gotten a valid event */
+
+ if (verbose)
+ print_event(&event);
+
+ /* handle the event appropriately */
+ switch (event.ue_type) {
+ case USB_EVENT_CTRLR_ATTACH:
+ if (verbose)
+ printf("USB_EVENT_CTRLR_ATTACH\n");
+ break;
+ case USB_EVENT_CTRLR_DETACH:
+ if (verbose)
+ printf("USB_EVENT_CTRLR_DETACH\n");
+ break;
+ case USB_EVENT_DEVICE_ATTACH:
+ case USB_EVENT_DEVICE_DETACH:
+ if (find_action(&event.u.ue_device, &action_match) == 0)
+ /* nothing found */
+ break;
+
+ if (verbose >= 2)
+ print_action(action_match.action, 0);
+
+ if (action_match.devname) {
+ if (verbose >= 2)
+ printf("%s: Setting DEVNAME='%s'\n",
+ __progname, action_match.devname);
+
+ error = setenv("DEVNAME", action_match.devname, 1);
+ if (error)
+ fprintf(stderr, "%s: setenv(\"DEVNAME\", \"%s\",1) failed, %s\n",
+ __progname, action_match.devname, strerror(errno));
+ }
+
+ if (USB_EVENT_IS_ATTACH(event.ue_type) &&
+ action_match.action->attach)
+ execute_command(action_match.action->attach);
+ if (USB_EVENT_IS_DETACH(event.ue_type) &&
+ action_match.action->detach)
+ execute_command(action_match.action->detach);
+ break;
+ case USB_EVENT_DRIVER_ATTACH:
+ if (verbose)
+ printf("USB_EVENT_DRIVER_ATTACH\n");
+ break;
+ case USB_EVENT_DRIVER_DETACH:
+ if (verbose)
+ printf("USB_EVENT_DRIVER_DETACH\n");
+ break;
+ default:
+ printf("Unknown USB event %d\n", event.ue_type);
+ }
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int error, i;
+ int ch; /* getopt option */
+ int debug = 0; /* print debugging output */
+ int explore_once = 0; /* don't do only explore */
+ int handle_events = 1; /* do handle the event queue */
+ int maxfd; /* maximum fd in use */
+ char buf[50]; /* for creation of the filename */
+ fd_set r,w;
+ int itimeout = TIMEOUT; /* timeout for select */
+ struct timeval tv;
+
+ if (modfind(USB_UHUB) < 0) {
+ if (kldload(USB_KLD) < 0 || modfind(USB_UHUB) < 0) {
+ perror(USB_KLD ": Kernel module not available");
+ return 1;
+ }
+ }
+
+ while ((ch = getopt(argc, argv, "c:def:nt:v")) != -1) {
+ switch(ch) {
+ case 'c':
+ configfile = strdup(optarg);
+ if (configfile == NULL) {
+ fprintf(stderr, "strdup returned NULL\n");
+ return 1;
+ }
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'e':
+ explore_once = 1;
+ break;
+ case 'f':
+ if (ndevs < MAXUSBDEV)
+ devs[ndevs++] = optarg;
+ break;
+ case 'n':
+ handle_events = 0;
+ break;
+ case 't':
+ itimeout = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ maxfd = 0;
+ if (ndevs == 0) {
+ /* open all the USBDEVS\d+ devices */
+ for (i = 0; i < MAXUSBDEV; i++) {
+ sprintf(buf, "%s%d", USBDEV, i);
+ fds[ndevs] = open(buf, O_RDWR);
+ if (fds[ndevs] >= 0) {
+ devs[ndevs] = strdup(buf);
+ if (devs[ndevs] == NULL) {
+ fprintf(stderr, "strdup returned NULL\n");
+ return 1;
+ }
+ if (verbose)
+ printf("%s: opened %s\n",
+ __progname, devs[ndevs]);
+ if (fds[ndevs] > maxfd)
+ maxfd = fds[ndevs];
+ ndevs++;
+ } else if (errno != ENXIO && errno != ENOENT) {
+ /* there was an error, on a device that does
+ * exist (device is configured)
+ */
+ fprintf(stderr, "%s: Could not open %s, %s\n",
+ __progname, buf, strerror(errno));
+ exit(1);
+ }
+ }
+ } else {
+ /* open all the files specified with -f */
+ for (i = 0; i < ndevs; i++) {
+ fds[i] = open(devs[i], O_RDWR);
+ if (fds[i] < 0) {
+ fprintf(stderr, "%s: Could not open %s, %s\n",
+ __progname, devs[i], strerror(errno));
+ exit(1);
+ } else {
+ if (verbose)
+ printf("%s: opened %s\n",
+ __progname, devs[i]);
+ if (fds[i] > maxfd)
+ maxfd = fds[i];
+ }
+ }
+ }
+
+ if (ndevs == 0) {
+ fprintf(stderr, "No USB host controllers found\n");
+ exit(1);
+ }
+
+
+ /* Do the explore once and exit */
+ if (explore_once) {
+ for (i = 0; i < ndevs; i++) {
+ error = ioctl(fds[i], USB_DISCOVER);
+ if (error < 0) {
+ fprintf(stderr, "%s: ioctl(%s, USB_DISCOVER) "
+ "failed, %s\n",
+ __progname, devs[i], strerror(errno));
+ exit(1);
+ }
+ }
+ exit(0);
+ }
+
+ if (handle_events) {
+ if (verbose)
+ printf("%s: reading configuration file %s\n",
+ __progname, configfile);
+ read_configuration();
+
+ fd = open(USBDEV, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ fprintf(stderr, "%s: Could not open %s, %s\n",
+ __progname, USBDEV, strerror(errno));
+ exit(1);
+ }
+ if (verbose)
+ printf("%s: opened %s\n", __progname, USBDEV);
+ if (fd > maxfd)
+ maxfd = fd;
+
+ process_event_queue(fd); /* dequeue the initial events */
+ }
+
+ /* move to the background */
+ if (!debug)
+ daemon(0, 0);
+
+ /* start select on all the open file descriptors */
+ for (;;) {
+ FD_ZERO(&r);
+ FD_ZERO(&w);
+ if (handle_events)
+ FD_SET(fd, &r); /* device USBDEV */
+ for (i = 0; i < ndevs; i++)
+ FD_SET(fds[i], &w); /* device USBDEV\d+ */
+ tv.tv_usec = 0;
+ tv.tv_sec = itimeout;
+ error = select(maxfd+1, &r, &w, 0, itimeout ? &tv : 0);
+ if (error < 0) {
+ fprintf(stderr, "%s: Select failed, %s\n",
+ __progname, strerror(errno));
+ exit(1);
+ }
+
+ /* USBDEV\d+ devices have signaled change, do a usb_discover */
+ for (i = 0; i < ndevs; i++) {
+ if (error == 0 || FD_ISSET(fds[i], &w)) {
+ if (verbose >= 2)
+ printf("%s: doing %sdiscovery on %s\n",
+ __progname,
+ (error? "":"timeout "), devs[i]);
+ if (ioctl(fds[i], USB_DISCOVER) < 0) {
+ fprintf(stderr, "%s: ioctl(%s, "
+ "USB_DISCOVER) failed, %s\n",
+ __progname, devs[i],
+ strerror(errno));
+ exit(1);
+ }
+ }
+ }
+
+ /* check the event queue */
+ if (handle_events && (FD_ISSET(fd, &r) || error == 0)) {
+ if (verbose >= 2)
+ printf("%s: processing event queue %son %s\n",
+ __progname,
+ (error? "":"due to timeout "), USBDEV);
+ process_event_queue(fd);
+ }
+ }
+}
diff --git a/usr.sbin/usbd/usbd.conf.5 b/usr.sbin/usbd/usbd.conf.5
new file mode 100644
index 0000000..decfe3e
--- /dev/null
+++ b/usr.sbin/usbd/usbd.conf.5
@@ -0,0 +1,163 @@
+.\"
+.\" Copyright (c) 1999 Nick Hibma. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce 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$
+.\"
+.\" Many parts of this manual have been snarfed from the pccard.conf (5) man
+.\" page, copyright by Andrew McRae.
+.\"
+.Dd November 19, 1999
+.Dt USBD.CONF 5
+.Os
+.Sh NAME
+.Nm usbd.conf
+.Nd
+.Xr usbd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr usbd 8
+daemon.
+It provides information to allow execution of userland commands
+on events reported by the
+.Xr usb 4
+subsystem in the kernel.
+Currently the only events are device attach and
+detach, but could in the future be extended to include power management
+functions.
+.Pp
+The configuration file consists of a sorted list of entries.
+Each entry
+describes a set of criteria commands.
+When an event occurs, the criteria
+are checked and if met, the commands for that event are executed through
+a shell.
+The list is sorted and scanned from top to bottom.
+The first
+matching entry is used for an event.
+.Pp
+Each entry contains a number of fields.
+There are 3 types of fields:
+descriptive fields, selection criteria and commands to execute on
+events.
+The field names are case sensitive and should be all lower case.
+Each field can have one or more arguments.
+.Pp
+The following fields are available:
+.Bl -tag -width devicename\ <Id>
+.It device Ar string
+Start a new entry.
+.Ar string
+is an arbitrary string used for pretty printing.
+.It product Ar id
+Product Id
+.It vendor Ar id
+Vendor Id
+.It release Ar id
+Release Id, also called revision Id sometimes.
+.It class Ar id
+Device Class
+.It subclass Ar id
+Device Subclass
+.It protocol Ar id
+Device Protocol
+.It devname Ar string
+Device name, for example umass2, or ums0.
+These device names can contain regular expressions.
+See
+.Xr regex 3
+and
+.Xr re_format 7 .
+The device name that is matched can be used in the commands below
+through adding ${DEVNAME} somewhere in that string.
+.El
+.Pp
+String arguments may be quoted.
+If a string argument contains a space or
+tab character it needs to be enclosed in single or double quotes.
+If an
+argument contains a single or double quote, that quote needs to be
+enclosed in double or single quotes respectively.
+See below for
+examples.
+.Pp
+Numeric arguments can either be specified in decimal (42), octal (052) or
+hexadecimal (0x2a).
+.Pp
+The values for the fields
+.Li product , vendor , release, class , subclass
+and
+.Li protocol
+can be retrieved by killing the
+.Nm usbd
+daemon and running it with the
+.Fl d
+and
+.Fl v
+flags.
+.Pp
+Commands to be executed when the action is matched:
+.Bl -tag -width devicename\ <Id>
+.It attach Ar string
+Shell command to execute when a device is attached.
+.It detach Ar string
+Shell command to execute when a device is detached.
+.El
+.Sh EXAMPLES
+A sample entry to rescan the SCSI bus on connection of a
+.Tn "Iomega USB Zip Drive" :
+.Bd -literal
+ device "USB Zip drive"
+ product 0x0001
+ vendor 0x059b
+ release 0x0100
+ attach "/usr/bin/camcontrol rescan bus 0"
+.Ed
+.Pp
+To start up moused for a newly attached mouse:
+.Bd -literal
+ device "Mouse"
+ devname "ums[0-9]+"
+ attach "/usr/sbin/moused -p /dev/${DEVNAME} -I /var/run/moused.${DEVNAME}.pid"
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/pccard.conf -compact
+.It Pa /etc/usbd.conf
+The
+.Nm usbd
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr usb 4 ,
+.Xr usbd 8 ,
+.Xr usbdevs 8
+.Sh BUGS
+It is currently not possible to use a selection criterion more than once.
+For example, it is not possible to specify more than one vendor ID.
+.Sh AUTHORS
+This manual page was written by
+.An Nick Hibma Aq n_hibma@FreeBSD.org .
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..640dd51
--- /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] [-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..8c8f6bb
--- /dev/null
+++ b/usr.sbin/vidcontrol/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= vidcontrol
+SRCS= vidcontrol.c decode.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vidcontrol/decode.c b/usr.sbin/vidcontrol/decode.c
new file mode 100644
index 0000000..fddc3d5
--- /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", &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..9bf881c
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.1
@@ -0,0 +1,533 @@
+.\"
+.\" 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 May 27, 2002
+.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
+command 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.
+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
+.Li 25 line modes Ta 8x16 (VGA), 8x14 (EGA)
+.Li 30 line modes Ta 8x16
+.Li 43 line modes Ta 8x8
+.Li 50 line modes Ta 8x8
+.Li 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 the raster text mode
+.Ar VESA_800x600 ,
+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 terminal device.
+.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, 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
+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
+.Li scr2*
+utilities in the
+.Li graphics
+and
+.Li textproc
+categories of the
+.Em "Ports Collection" .
+.Sh AUTHORS
+.An S\(/oren Schmidt Aq sos@FreeBSD.org
+.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..3ce1f21
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.c
@@ -0,0 +1,870 @@
+/*-
+ * Copyright (c) 1994-1996 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 <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 _VESA_800x600_DFL_COLS 80
+#define _VESA_800x600_DFL_ROWS 25
+#define _VESA_800x600_DFL_FNSZ 16
+
+/* 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"
+};
+int hex = 0;
+int number;
+int vesa_cols = _VESA_800x600_DFL_COLS;
+int vesa_rows = _VESA_800x600_DFL_ROWS;
+char letter;
+struct vid_info info;
+
+
+static void
+usage()
+{
+ 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);
+}
+
+char *
+nextarg(int ac, char **av, int *indp, int oc, int strict)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+ if (strict != 0)
+ errx(1, "option requires two arguments -- %c", oc);
+ return(NULL);
+}
+
+FILE *
+openguess(char *a[], char *b[], char *c[], 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);
+}
+
+void
+load_scrnmap(char *filename)
+{
+ FILE *fd;
+ int size;
+ char *name;
+ scrmap_t scrnmap;
+ char *a[] = {"", SCRNMAP_PATH, NULL};
+ char *b[] = {filename, NULL};
+ char *c[] = {"", ".scm", NULL};
+ char *d[] = {"", NULL};
+
+ fd = openguess(a, b, c, d, &name);
+ if (fd == NULL) {
+ warn("screenmap file not found");
+ return;
+ }
+ size = sizeof(scrnmap);
+ if (decode(fd, (char *)&scrnmap, size) != size) {
+ rewind(fd);
+ if (fread(&scrnmap, 1, size, fd) != size) {
+ warnx("bad screenmap file");
+ fclose(fd);
+ return;
+ }
+ }
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
+ warn("can't load screenmap");
+ fclose(fd);
+}
+
+void
+load_default_scrnmap()
+{
+ scrmap_t scrnmap;
+ int i;
+
+ for (i=0; i<256; i++)
+ *((char*)&scrnmap + i) = i;
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
+ warn("can't load default screenmap");
+}
+
+void
+print_scrnmap()
+{
+ unsigned char map[256];
+ int i;
+
+ if (ioctl(0, GIO_SCRNMAP, &map) < 0) {
+ warn("getting screenmap");
+ return;
+ }
+ for (i=0; i<sizeof(map); i++) {
+ if (i > 0 && i % 16 == 0)
+ fprintf(stdout, "\n");
+ if (hex)
+ fprintf(stdout, " %02x", map[i]);
+ else
+ fprintf(stdout, " %03d", map[i]);
+ }
+ fprintf(stdout, "\n");
+
+}
+
+int
+fsize(FILE *file)
+{
+ struct stat sb;
+
+ if (fstat(fileno(file), &sb) == 0)
+ return sb.st_size;
+ else
+ return -1;
+}
+
+#define DATASIZE(x) ((x).w * (x).h * 256 / 8)
+
+void
+load_font(char *type, 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];
+ char *a[] = {"", FONT_PATH, NULL};
+ char *b[] = {filename, NULL};
+ char *c[] = {"", size_sufx, NULL};
+ 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) {
+ 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) {
+ warn("%s: can't load font file", filename);
+ return;
+ }
+ 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;
+ }
+
+ if (size == 0) {
+ warnx("%s: bad font size specification", type);
+ fclose(fd);
+ return;
+ }
+ } 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;
+ j = 2; /* XXX */
+ break;
+ }
+
+ if (size == 0) {
+ warnx("%s: can't guess font size", filename);
+ fclose(fd);
+ return;
+ }
+ rewind(fd);
+ }
+
+ fontmap = (char*) malloc(size);
+ if (decode(fd, fontmap, size) != size) {
+ rewind(fd);
+ if (fsize(fd) != size || fread(fontmap, 1, size, fd) != size) {
+ warnx("%s: bad font file", filename);
+ fclose(fd);
+ free(fontmap);
+ return;
+ }
+ }
+ if (ioctl(0, io, fontmap) < 0)
+ warn("can't load font");
+ fclose(fd);
+ free(fontmap);
+}
+
+void
+set_screensaver_timeout(char *arg)
+{
+ int nsec;
+
+ if (!strcmp(arg, "off"))
+ nsec = 0;
+ else {
+ nsec = atoi(arg);
+ if ((*arg == '\0') || (nsec < 1)) {
+ warnx("argument must be a positive number");
+ return;
+ }
+ }
+ if (ioctl(0, CONS_BLANKTIME, &nsec) == -1)
+ warn("setting screensaver period");
+}
+
+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 {
+ warnx("argument to -c must be normal, blink or destructive");
+ return;
+ }
+ ioctl(0, CONS_CURSORTYPE, &type);
+}
+
+int
+video_mode(int argc, char **argv, int *index)
+{
+ static struct {
+ char *name;
+ unsigned long mode;
+ } modes[] = {
+ { "80x25", SW_TEXT_80x25 },
+ { "80x30", SW_TEXT_80x30 },
+ { "80x43", SW_TEXT_80x43 },
+ { "80x50", SW_TEXT_80x50 },
+ { "80x60", SW_TEXT_80x60 },
+ { "132x25", SW_TEXT_132x25 },
+ { "132x30", SW_TEXT_132x30 },
+ { "132x43", SW_TEXT_132x43 },
+ { "132x50", SW_TEXT_132x50 },
+ { "132x60", SW_TEXT_132x60 },
+ { "VGA_40x25", SW_VGA_C40x25 },
+ { "VGA_80x25", SW_VGA_C80x25 },
+ { "VGA_80x30", SW_VGA_C80x30 },
+ { "VGA_80x50", SW_VGA_C80x50 },
+ { "VGA_80x60", SW_VGA_C80x60 },
+#ifdef SW_VGA_C90x25
+ { "VGA_90x25", SW_VGA_C90x25 },
+ { "VGA_90x30", SW_VGA_C90x30 },
+ { "VGA_90x43", SW_VGA_C90x43 },
+ { "VGA_90x50", SW_VGA_C90x50 },
+ { "VGA_90x60", SW_VGA_C90x60 },
+#endif
+ { "VGA_320x200", SW_VGA_CG320 },
+ { "EGA_80x25", SW_ENH_C80x25 },
+ { "EGA_80x43", SW_ENH_C80x43 },
+ { "VESA_132x25", SW_VESA_C132x25 },
+ { "VESA_132x43", SW_VESA_C132x43 },
+ { "VESA_132x50", SW_VESA_C132x50 },
+ { "VESA_132x60", SW_VESA_C132x60 },
+ { "VESA_800x600", SW_VESA_800x600 },
+ { NULL },
+ };
+ 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");
+ if (*index < argc) {
+ for (i = 0; modes[i].name != NULL; ++i) {
+ if (!strcmp(argv[*index], modes[i].name)) {
+ mode = modes[i].mode;
+ break;
+ }
+ }
+ if (modes[i].name == NULL)
+ return EXIT_FAILURE;
+ if (ioctl(0, mode, NULL) < 0) {
+ warn("cannot set videomode");
+ return EXIT_FAILURE;
+ }
+ if (mode == SW_VESA_800x600) {
+ /* columns */
+ if ((vesa_cols * 8 > 800) || (vesa_cols <= 0)) {
+ warnx("incorrect number of columns: %d",
+ vesa_cols);
+ size[0] = _VESA_800x600_DFL_COLS;
+ } else {
+ size[0] = vesa_cols;
+ }
+ /* rows */
+ if ((vesa_rows * _VESA_800x600_DFL_FNSZ > 600) ||
+ (vesa_rows <=0)) {
+ warnx("incorrect number of rows: %d",
+ vesa_rows);
+ size[1] = _VESA_800x600_DFL_ROWS;
+ } else {
+ size[1] = vesa_rows;
+ }
+ /* font size */
+ size[2] = _VESA_800x600_DFL_FNSZ;
+ 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);
+ warnc(ioerr, "cannot activate raster display");
+ return EXIT_FAILURE;
+ }
+ }
+ (*index)++;
+ }
+ return EXIT_SUCCESS;
+}
+
+int
+get_color_number(char *color)
+{
+ int i;
+
+ for (i=0; i<16; i++)
+ if (!strcmp(color, legal_colors[i]))
+ return i;
+ return -1;
+}
+
+void
+set_normal_colors(int argc, char **argv, int *index)
+{
+ int color;
+
+ if (*index < argc && (color = get_color_number(argv[*index])) != -1) {
+ (*index)++;
+ fprintf(stderr, "[=%dF", color);
+ if (*index < argc
+ && (color = get_color_number(argv[*index])) != -1
+ && color < 8) {
+ (*index)++;
+ fprintf(stderr, "[=%dG", color);
+ }
+ }
+}
+
+void
+set_reverse_colors(int argc, char **argv, int *index)
+{
+ int color;
+
+ if ((color = get_color_number(argv[*(index)-1])) != -1) {
+ fprintf(stderr, "[=%dH", color);
+ if (*index < argc
+ && (color = get_color_number(argv[*index])) != -1
+ && color < 8) {
+ (*index)++;
+ fprintf(stderr, "[=%dI", color);
+ }
+ }
+}
+
+void
+set_console(char *arg)
+{
+ int n;
+
+ if( !arg || strspn(arg,"0123456789") != strlen(arg)) {
+ warnx("bad console number");
+ return;
+ }
+
+ n = atoi(arg);
+ if (n < 1 || n > 16) {
+ warnx("console number out of range");
+ } else if (ioctl(0, VT_ACTIVATE, (caddr_t) (long) n) == -1)
+ warn("ioctl(VT_ACTIVATE)");
+}
+
+void
+set_border_color(char *arg)
+{
+ int color;
+
+ if ((color = get_color_number(arg)) != -1) {
+ fprintf(stderr, "[=%dA", color);
+ }
+ else
+ usage();
+}
+
+void
+set_mouse_char(char *arg)
+{
+ struct mouse_info mouse;
+ long l;
+
+ l = strtol(arg, NULL, 0);
+ if ((l < 0) || (l > UCHAR_MAX - 3)) {
+ warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3);
+ return;
+ }
+ mouse.operation = MOUSE_MOUSECHAR;
+ mouse.u.mouse_char = (int)l;
+ ioctl(0, CONS_MOUSECTL, &mouse);
+}
+
+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 {
+ warnx("argument to -m must be either on or off");
+ return;
+ }
+ ioctl(0, CONS_MOUSECTL, &mouse);
+}
+
+void
+set_lockswitch(char *arg)
+{
+ int data;
+
+ if (!strcmp(arg, "off"))
+ data = 0x01;
+ else if (!strcmp(arg, "on"))
+ data = 0x02;
+ else {
+ warnx("argument to -S must be either on or off");
+ return;
+ }
+ if (ioctl(0, VT_LOCKSWITCH, &data) == -1)
+ warn("ioctl(VT_LOCKSWITCH)");
+}
+
+static char
+*adapter_name(int type)
+{
+ static struct {
+ int type;
+ 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;
+}
+
+void
+show_adapter_info(void)
+{
+ struct video_adapter_info ad;
+
+ ad.va_index = 0;
+ if (ioctl(0, CONS_ADPINFO, &ad)) {
+ warn("failed to obtain adapter information");
+ return;
+ }
+
+ 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%x, buffer size:0x%x\n",
+ ad.va_window, ad.va_buffer_size);
+ printf(" window size:0x%x, 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%x\n", ad.va_unused0);
+}
+
+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%05x %2dk %2dk",
+ info.vi_window, (int)info.vi_window_size/1024,
+ (int)info.vi_window_gran/1024);
+ printf(" 0x%08x %dk\n",
+ info.vi_buffer, (int)info.vi_buffer_size/1024);
+ }
+}
+
+void
+show_info(char *arg)
+{
+ if (!strcmp(arg, "adapter"))
+ show_adapter_info();
+ else if (!strcmp(arg, "mode"))
+ show_mode_info();
+ else {
+ warnx("argument to -i must be either adapter or mode");
+ return;
+ }
+}
+
+void
+test_frame()
+{
+ int i;
+
+ fprintf(stdout, "[=0G\n\n");
+ for (i=0; i<8; i++) {
+ fprintf(stdout, "[=15F[=0G %2d [=%dF%-16s"
+ "[=15F[=0G %2d [=%dF%-16s "
+ "[=15F %2d [=%dGBACKGROUND[=0G\n",
+ i, i, legal_colors[i], i+8, i+8,
+ legal_colors[i+8], i, i);
+ }
+ fprintf(stdout, "[=%dF[=%dG[=%dH[=%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.
+ */
+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) {
+ warn("failed to obtain 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) {
+ warn("failed to allocate memory for dump");
+ return;
+ }
+
+ if (ioctl(0, CONS_SCRSHOT, &shot) == -1) {
+ warn("failed to get dump of the screen");
+ return;
+ }
+
+ if (mode == DUMP_FMT_RAW) {
+ printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2,
+ shot.xsize, shot.ysize);
+ fflush(stdout);
+
+ (void)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) {
+ warn("failed to allocate memory for line buffer");
+ return;
+ }
+
+ 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);
+ }
+
+ return;
+}
+
+void
+set_history(char *opt)
+{
+ int size;
+
+ size = atoi(opt);
+ if ((*opt == '\0') || size < 0) {
+ warnx("argument must be a positive number");
+ return;
+ }
+ if (ioctl(0, CONS_HISTORY, &size) == -1)
+ warn("setting history buffer size");
+}
+
+void
+clear_history()
+{
+
+ if (ioctl(0, CONS_CLRHIST) == -1)
+ warn("clear history buffer");
+}
+
+int
+main(int argc, char **argv)
+{
+ char *font, *type;
+ int dumpmod, dumpopt, opt;
+ int reterr;
+
+ info.size = sizeof(info);
+ if (argc == 1)
+ usage();
+ /* Not reached */
+ if (ioctl(0, CONS_GETINFO, &info) < 0)
+ 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) {
+ 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':
+ set_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);
+ set_normal_colors(argc, argv, &optind);
+ if (optind < argc && !strcmp(argv[optind], "show")) {
+ test_frame();
+ optind++;
+ }
+ 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..539a22f
--- /dev/null
+++ b/usr.sbin/vipw/vipw.8
@@ -0,0 +1,111 @@
+.\" 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.
+.\"
+.\" @(#)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 .
+.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 pwd_mkdb 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
diff --git a/usr.sbin/vipw/vipw.c b/usr.sbin/vipw/vipw.c
new file mode 100644
index 0000000..152a454
--- /dev/null
+++ b/usr.sbin/vipw/vipw.c
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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/vnconfig/Makefile b/usr.sbin/vnconfig/Makefile
new file mode 100644
index 0000000..c7aa34b
--- /dev/null
+++ b/usr.sbin/vnconfig/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= vnconfig
+NOMAN= "mdconfig supplies manpage"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vnconfig/vnconfig.c b/usr.sbin/vnconfig/vnconfig.c
new file mode 100644
index 0000000..294250d
--- /dev/null
+++ b/usr.sbin/vnconfig/vnconfig.c
@@ -0,0 +1,25 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(int argc, char **argv)
+{
+
+ fprintf(stderr, "ERROR: vnconfig(8) has been discontinued\n");
+ fprintf(stderr, "\tPlease use mdconfig(8).\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..6cc0e11
--- /dev/null
+++ b/usr.sbin/watch/watch.8
@@ -0,0 +1,116 @@
+.\"
+.\" $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 BUGS
+No terminal emulation is performed.
+All user output is reproduced as-is.
+.Sh AUTHORS
+.An Ugen J.S. Antsilevich Aq ugen@NetVision.net.il
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.1 .
diff --git a/usr.sbin/watch/watch.c b/usr.sbin/watch/watch.c
new file mode 100644
index 0000000..1e630db
--- /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 <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;
+dev_t snp_tty;
+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)
+{
+ char snp[] = {_PATH_DEV "snpX"};
+ char c;
+ int f, mode, pos;
+
+ pos = strlen(snp) - 1;
+ if (opt_write)
+ mode = O_RDWR;
+ else
+ mode = O_RDONLY;
+
+ if (opt_snpdev == NULL)
+ for (c = '0'; c <= '9'; c++) {
+ snp[pos] = c;
+ if ((f = open(snp, mode)) < 0)
+ continue;
+ return f;
+ }
+ else
+ if ((f = open(opt_snpdev, mode)) != -1)
+ return (f);
+ fatal(EX_OSFILE, "cannot open snoop device");
+ return (0);
+}
+
+
+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)
+{
+ dev_t dev;
+
+ dev = NODEV;
+ ioctl(snp_io, SNPSTTY, &dev);
+}
+
+static void
+attach_snp(void)
+{
+ if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
+ fatal(EX_UNAVAILABLE, "cannot attach to 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");
+
+ snp_tty = sb.st_rdev;
+ 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);
+
+ setup_scr();
+ snp_io = open_snp();
+
+ 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..68675c7
--- /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
+DPADD= ${LIBM}
+
+.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..f305b1f
--- /dev/null
+++ b/usr.sbin/watchdogd/watchdog.8
@@ -0,0 +1,70 @@
+.\" 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 February 28, 2004
+.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
+enables debugging.
+.Pp
+The
+.Fl t Ar timeout
+specifies the desired timeout period in seconds, a value of
+zero will disable the watchdog.
+.Sh SEE ALSO
+.Xr watchdog 4 ,
+.Xr watchdogd 8 ,
+.Xr watchdog 9
+.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 .
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.1 .
diff --git a/usr.sbin/watchdogd/watchdogd.8 b/usr.sbin/watchdogd/watchdogd.8
new file mode 100644
index 0000000..e20ec56
--- /dev/null
+++ b/usr.sbin/watchdogd/watchdogd.8
@@ -0,0 +1,126 @@
+.\" 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 June 25, 2003
+.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.
+.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 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 .
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.1 .
diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c
new file mode 100644
index 0000000..0c1b625
--- /dev/null
+++ b/usr.sbin/watchdogd/watchdogd.c
@@ -0,0 +1,277 @@
+/*
+ * 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/rtprio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/watchdog.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.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(void);
+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;
+ FILE *fp;
+
+ 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);
+
+ if (debugging == 0 && daemon(0, 0) == -1) {
+ watchdog_onoff(0);
+ err(EX_OSERR, "daemon");
+ }
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+
+ fp = fopen(pidfile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ watchdog_loop();
+
+ /* exiting */
+ watchdog_onoff(0);
+ unlink(pidfile);
+ return (EX_OK);
+ } else {
+ if (passive)
+ timeout |= WD_PASSIVE;
+ else
+ timeout |= WD_ACTIVE;
+ if (watchdog_patpat() < 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 == 0) {
+ failed = 0;
+
+ if (test_cmd != NULL)
+ failed = system(test_cmd);
+ else
+ failed = stat("/etc", &sb);
+
+ if (failed == 0)
+ watchdog_patpat();
+ sleep(nap);
+ }
+}
+
+/*
+ * Reset the watchdog timer. This function must be called periodically
+ * to keep the watchdog from firing.
+ */
+int
+watchdog_patpat(void)
+{
+
+ return ioctl(fd, WDIOCPATPAT, &timeout);
+}
+
+/*
+ * Toggle the kernel's watchdog. This routine is used to enable and
+ * disable the watchdog.
+ */
+static int
+watchdog_onoff(int onoff)
+{
+
+ if (onoff)
+ timeout |= WD_ACTIVE;
+ else
+ timeout &= ~WD_ACTIVE;
+ return watchdog_patpat();
+}
+
+/*
+ * Tell user how to use the program.
+ */
+static void
+usage()
+{
+ if (is_daemon)
+ fprintf(stderr, "usage: watchdogd [-d] [-e cmd] [-I file]\n");
+ else
+ fprintf(stderr, "usage: watchdog [-d] [-t]\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 (is_daemon && timeout < WD_TO_1SEC)
+ errx(EX_USAGE, "-t argument is less than one second.");
+}
diff --git a/usr.sbin/wicontrol/Makefile b/usr.sbin/wicontrol/Makefile
new file mode 100644
index 0000000..b8ac802
--- /dev/null
+++ b/usr.sbin/wicontrol/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= wicontrol
+MAN= wicontrol.8
+
+WARNS?= 2
+CFLAGS+= -DWICACHE
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wicontrol/wicontrol.8 b/usr.sbin/wicontrol/wicontrol.8
new file mode 100644
index 0000000..a0540c9
--- /dev/null
+++ b/usr.sbin/wicontrol/wicontrol.8
@@ -0,0 +1,587 @@
+.\" 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 4, 2003
+.Dt WICONTROL 8
+.Os
+.Sh NAME
+.Nm wicontrol
+.Nd "configure Lucent, Intersil, and Atheros wireless devices"
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Ar iface Op Fl o
+.Nm
+.Op Fl i
+.Ar iface Fl l
+(dump associated stations)
+.Nm
+.Op Fl i
+.Ar iface Fl L
+(list available access points)
+.Nm
+.Op Fl i
+.Ar iface Fl t Ar tx_rate
+.Nm
+.Op Fl i
+.Ar iface Fl n Ar network_name
+.Nm
+.Op Fl i
+.Ar iface Fl s Ar station_name
+.Nm
+.Op Fl i
+.Ar iface Fl c Cm 0 | 1
+.Nm
+.Op Fl i
+.Ar iface Fl q Ar SSID
+.Nm
+.Op Fl i
+.Ar iface Fl p Ar port_type
+.Nm
+.Op Fl i
+.Ar iface Fl a Ar access_point_density
+.Nm
+.Op Fl i
+.Ar iface Fl m Ar mac_address
+.Nm
+.Op Fl i
+.Ar iface Fl d Ar max_data_length
+.Nm
+.Op Fl i
+.Ar iface Fl e Cm 0 | 1
+.Nm
+.Op Fl i
+.Ar iface Fl k Ar key
+.Op Fl v Cm 1 | 2 | 3 | 4
+.Nm
+.Op Fl i
+.Ar iface Fl T Cm 1 | 2 | 3 | 4
+.Nm
+.Op Fl i
+.Ar iface Fl r Ar RTS_threshold
+.Nm
+.Op Fl i
+.Ar iface Fl f Ar frequency
+.Nm
+.Op Fl i
+.Ar iface Fl P Cm 0 | 1
+.Nm
+.Op Fl i
+.Ar iface Fl S Ar max_sleep_duration
+.Nm
+.Op Fl i
+.Ar iface Fl Z
+(zero signal cache)
+.Nm
+.Op Fl i
+.Ar iface Fl C
+(display signal cache)
+.Sh DESCRIPTION
+The
+.Nm
+utility controls the operation of Lucent, Intersil, and Atheros-based wireless
+networking devices via
+.Xr wi 4
+or
+.Xr ath 4
+driver.
+.Pp
+You should not use this program to configure IEEE 802.11 parameters.
+Use
+.Xr ifconfig 8
+instead to do those tasks (i.e., set SSID, WEP key, etc.).
+.Pp
+The
+.Nm
+utility can also be used to view the current settings of these parameters,
+dump out the values of the card's statistics counters, list associated
+stations (in HostAP mode), and scan for available access points.
+.Pp
+The
+.Ar iface
+argument given to
+.Nm
+should be the logical interface name associated with the Lucent, Intersil,
+and Atheros device
+.Li ( wi0 , wi1 , ath0 ,
+etc.).
+If none is specified then
+.Dq Li wi0
+is used as default.
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Oo Fl i Oc Ar iface Op Fl o
+Display the current settings of the specified wireless interface.
+This retrieves the current card settings from the driver and prints them
+out.
+Using the additional
+.Fl o
+flag will cause
+.Nm
+to print out the statistics counters instead of the card settings.
+Encryption keys are only displayed if
+.Nm
+is run as root.
+.It Oo Fl i Oc Ar iface Fl a Ar access_point_density
+Specify the
+access point density
+for a given interface.
+Legal values are
+.Cm 1
+(low),
+.Cm 2
+(medium) and
+.Cm 3
+(high).
+This setting influences some of the radio modem threshold settings.
+.It Oo Fl i Oc 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.,
+.Dq Li 00:60:1d:12:34:56 .
+This programs the new address into the card
+and updates the interface as well.
+.It Oo Fl i Oc Ar iface Fl d Ar max_data_length
+Set the maximum receive and transmit frame size for a specified interface.
+The
+.Ar max_data_length
+can be any number from 350 to 2304.
+The default is 2304.
+.It Oo Fl i Oc 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 2347.
+The default is 2347.
+.It Oo Fl i Oc Ar iface Fl Z
+Clear the signal strength cache maintained internally by the
+.Xr wi 4
+driver.
+.It Oo Fl i Oc Ar iface Fl C
+Display the cached signal strength information maintained by the
+.Xr wi 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.
+The signal quality value is produced by subtracting the noise level
+from the signal strength (i.e., less noise and better signal yields
+better signal quality).
+.El
+.Sh DEPRECATED AND OBSOLETE OPTIONS
+The
+.Nm
+utility has a number of options that are now deprecated or obsolete, as they
+have been overtaken by extensions to
+.Xr ifconfig 8
+and changes to the driver.
+The deprecated and obsolete options are as follows:
+.Bl -tag -width indent
+.It Oo Fl i Oc Ar iface Fl t Ar tx_rate
+This flag is deprecated.
+Use
+.Xr ifconfig 8
+.Cm mediaopt
+instead.
+.Pp
+Set the transmit rate of the specified interface.
+The legal values
+for the transmit rate vary depending on whether the interface is a
+standard WaveLAN/IEEE or a WaveLAN/IEEE Turbo adapter.
+The standard
+NICs support a maximum transmit rate of 2Mbps while the turbo NICs
+support a maximum speed of 6Mbps.
+The following table shows the
+legal transmit rate settings and the corresponding transmit speeds:
+.Bl -column ".Em TX\ rate" ".Em NIC\ speed" -offset indent
+.Em "TX rate NIC speed"
+.It Cm 1 Ta "Fixed Low (1Mbps)"
+.It Cm 2 Ta "Fixed Standard (2Mbps)"
+.It Cm 3 Ta "Auto Rate Select (High)"
+.It Cm 4 Ta "Fixed Medium (4Mbps)"
+.It Cm 5 Ta "Fixed High (6Mbps)"
+.It Cm 6 Ta "Auto Rate Select (Standard)"
+.It Cm 7 Ta "Auto Rate Select (Medium)"
+.El
+.Pp
+The default driver setting is
+.Cm 3
+(auto rate select).
+The numbers vary from card to card.
+.It Oo Fl i Oc Ar iface Fl n Ar network_name
+This flag is deprecated.
+Use
+.Xr ifconfig 8
+.Cm ssid
+or
+.Cm nwid
+instead.
+.Pp
+Set the name of the service set (IBSS) that this station wishes to
+join.
+The
+.Ar network_name
+can be any text string up to 30 characters in length.
+The default name
+is the string
+.Dq Li ANY
+which should allow the station to connect to the first
+available access point.
+The interface should be set for BSS mode using
+the
+.Fl p
+flag in order for this to work.
+.Pp
+Note: the WaveLAN manual indicates that an empty string will allow the
+host to connect to any access point, however I have also seen a reference
+in another driver which indicates that the
+.Dq Li ANY
+string works as well.
+.It Oo Fl i Oc Ar iface Fl s Ar station_name
+This flag is deprecated.
+Use
+.Xr ifconfig 8
+.Cm stationname
+or
+.Cm station
+instead.
+.Pp
+Sets the
+station name
+for the specified interface.
+The
+.Ar station_name
+is used for diagnostic purposes.
+The
+.Tn "Lucent WaveMANAGER"
+software can
+poll the names of remote hosts.
+.It Oo Fl i Oc Ar iface Fl c Cm 0 | 1
+This flag is deprecated.
+IBSS networks are automatically created on those cards whose firmware
+supports it while in IBSS mode.
+.Pp
+Allow the station to create a service set (IBSS).
+Permitted values are
+.Cm 0
+(don't create IBSS) and
+.Cm 1
+(enable creation of IBSS).
+The default is
+.Cm 0 .
+.Pp
+Only newer versions of the Lucent firmware support this.
+.It Oo Fl i Oc Ar iface Fl q Ar SSID
+This flag is deprecated.
+The
+.Cm ssid
+setting from
+.Xr ifconfig 8
+is the current preferred way of setting this parameter.
+.Pp
+Specify the name of an IBSS (SSID) to create on a given interface.
+The
+.Ar SSID
+can be any text string up to 30 characters long.
+.Pp
+Note: this option is provided for experimental purposes only: enabling
+the creation of an IBSS on a host system doesn't appear to actually work.
+.It Oo Fl i Oc Ar iface Fl p Ar port_type
+This flag is deprecated.
+It should never be used.
+Do not use this flag.
+Its meaning depends on the type of card you are using, as well as the
+firmware you have installed in the card in some cases.
+Beware.
+Danger.
+Do not use.
+Instead, use the
+.Xr ifconfig 8
+.Cm media
+and
+.Cm mediaopt
+commands.
+.Pp
+Set the
+port type
+for a specified interface.
+The legal values for
+.Ar port_type
+are
+.Cm 1
+(BSS mode) and
+.Cm 3
+(ad-hoc) 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 BSS mode,
+hosts must associate with a service set controlled by an access point,
+which relays traffic between end stations.
+The default setting is
+.Cm 1
+(BSS mode).
+Lucent cards have one set of meanings.
+Prism cards have another.
+Symbol cards have a third.
+Do not use this flag.
+.It Oo Fl i Oc Ar iface Fl e Cm 0 | 1
+This flag is deprecated.
+It has been replaced by the
+.Xr ifconfig 8
+.Cm wepmode
+option.
+.Pp
+Enable or disable WEP encryption.
+Permitted values are
+.Cm 0
+(encryption disabled) or
+.Cm 1
+(encryption enabled).
+Encryption is off by default.
+.Pp
+Both 128-bit and 64-bit WEP have been broken.
+See the
+.Sx BUGS
+section for details.
+.It Oo Fl i Oc Ar iface Fl k Ar key Op Fl v Cm 1 | 2 | 3 | 4
+This flag is obsolete.
+The
+.Xr ifconfig 8
+.Cm wepkey
+should be used instead.
+.Pp
+Set WEP encryption keys.
+There are four default encryption keys
+that can be programmed.
+A specific key can be set using
+the
+.Fl v
+flag.
+If the
+.Fl v
+flag is not specified, the first key will be set.
+Encryption keys
+can either be normal text (i.e.,
+.Dq Li hello )
+or a series of hexadecimal digits (i.e.,
+.Dq Li 0x1234512345 ) .
+For
+WaveLAN Turbo Silver cards, the key is restricted to 40 bits, hence
+the key can be either a 5 character text string or 10 hex digits.
+For WaveLAN Turbo Gold cards, the key can also be 104 bits,
+which means the key can be specified as either a 13 character text
+string or 26 hex digits in addition to the formats supported by the
+Silver cards.
+.Pp
+For maximum portability, hex keys are recommended;
+the mapping of text keys to WEP encryption is usually driver-specific.
+In particular, the
+.Tn Windows
+drivers do this mapping differently to
+.Fx .
+.Pp
+Note: Both 128-bit and 64-bit WEP encryption have been broken.
+See the
+.Sx BUGS
+section for details.
+.It Oo Fl i Oc Ar iface Fl T Cm 1 | 2 | 3 | 4
+This flag is obsolete.
+The
+.Xr ifconfig 8
+.Cm weptxkey
+should be used instead.
+.Pp
+Specify which of the four WEP encryption keys will be used to
+encrypt transmitted packets.
+.Pp
+Note: Both 128-bit and 64-bit WEP have been broken.
+See the
+.Sx BUGS
+section for details.
+.It Oo Fl i Oc Ar iface Fl f Ar frequency
+This flag is deprecated.
+Use
+.Xr ifconfig 8
+.Cm channel
+instead.
+.Pp
+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 2412 - 2412"
+.It Cm 2 Ta "2417 2417 - 2417"
+.It Cm 3 Ta "2422 2422 - 2422"
+.It Cm 4 Ta "2427 2427 - 2427"
+.It Cm 5 Ta "2432 2432 - 2432"
+.It Cm 6 Ta "2437 2437 - 2437"
+.It Cm 7 Ta "2442 2442 - 2442"
+.It Cm 8 Ta "2447 2447 - 2447"
+.It Cm 9 Ta "2452 2452 - 2452"
+.It Cm 10 Ta "2457 2457 2457 2457"
+.It Cm 11 Ta "2462 2462 2462 2462"
+.It Cm 12 Ta "- 2467 2467 2467"
+.It Cm 13 Ta "- 2472 2472 2472"
+.It Cm 14 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
+.Cm 3 .
+For NICs sold in France, the default channel is
+.Cm 11 .
+For NICs sold in Japan, the default channel is
+.Cm 14 ,
+and it is the only available channel for pre-11Mbps NICs.
+Note that two stations must be set to the same channel in order to
+communicate.
+.It Oo Fl i Oc Ar iface Fl P Cm 0 | 1
+This flag is obsolete.
+The
+.Xr ifconfig 8
+.Cm powersave
+should be used instead.
+.Pp
+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.
+Note that power
+management requires the cooperation of an access point in order to
+function; it is not functional in ad-hoc mode.
+Also, power management
+is only implemented in Lucent WavePOINT firmware version 2.03 or
+later, and in WaveLAN PCMCIA adapter firmware 2.00 or later.
+Older
+revisions will silently ignore the power management setting.
+Legal
+values for this parameter are
+.Cm 0
+(off) and
+.Cm 1
+(on).
+.It Oo Fl i Oc Ar iface Fl S Ar max_sleep_interval
+This flag is obsolete.
+The
+.Xr ifconfig 8
+.Cm powersleep
+should be used instead.
+.Pp
+Specify the sleep interval to use when power management is enabled.
+The
+.Ar max_sleep_interval
+is specified in milliseconds.
+The default is 100.
+.El
+.Sh SEE ALSO
+.Xr ath 4 ,
+.Xr awi 4 ,
+.Xr ipsec 4 ,
+.Xr wi 4 ,
+.Xr ifconfig 8
+.Sh BUGS
+There are deprecated flags here that duplicate functionality of
+.Xr ifconfig 8 .
+These flags were deprecated in
+.Fx 5.1
+and will be removed in a future release.
+.Pp
+The WEP encryption method has been broken so that third parties
+can recover the keys in use relatively quickly at distances that are
+surprising to most people.
+Do not rely on WEP for anything but the most basic, remedial security.
+IPSEC will give you a higher level of security and should be used
+whenever possible.
+Do not trust access points or wireless machines that connect through
+them as they can provide no assurance that the traffic is legitimate.
+MAC addresses can easily be forged and should therefore not be used as
+the only access control.
+.Pp
+The attack on WEP is a passive attack, requiring only the ability to
+sniff packets on the network.
+The passive attack can be launched at a distance larger, up to many
+miles, than one might otherwise expect given a specialized antenna
+used in point to point applications.
+The attacker can recover the keys from a 128-bit WEP network with only
+5,000,000 to 6,000,000 packets.
+While this may sound like a large number of packets, empirical
+evidence suggests that this amount of traffic is generated in a few
+hours on a partially loaded network.
+Once a key has been compromised, the only remedial action is to
+discontinue it and use a new key.
+.Pp
+See
+.Pa http://www.cs.rice.edu/~astubble/wep/wep_attack.html
+for details of the attack.
+Many programs to assist in cracking WEP keys are widely available.
+.Pp
+If you must use WEP, you are strongly encouraged to pick keys whose
+bytes are random and not confined to
+.Tn ASCII
+characters.
+Brute force attacks on WEP keys are also possible.
+Experience has showns that
+.Tn ASCII
+keys can be cracked in less than a day.
+Even random bytes can be cracked in less than two weeks.
+.Pp
+Signal cache is broken right now.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Bill Paul Aq wpaul@ctr.columbia.edu .
diff --git a/usr.sbin/wicontrol/wicontrol.c b/usr.sbin/wicontrol/wicontrol.c
new file mode 100644
index 0000000..b8b1add
--- /dev/null
+++ b/usr.sbin/wicontrol/wicontrol.c
@@ -0,0 +1,1239 @@
+/*
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] = "@(#) Copyright (c) 1997, 1998, 1999\
+ Bill Paul. All rights reserved.";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <dev/wi/if_wavelan_ieee.h>
+#include <dev/wi/if_wireg.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+
+static int wi_getval(const char *, struct wi_req *);
+static int wi_getvalmaybe(const char *, struct wi_req *);
+static void wi_setval(const char *, struct wi_req *);
+static void wi_printstr(struct wi_req *);
+static void wi_setstr(const char *, int, char *);
+static void wi_setbytes(const char *, int, char *, int);
+static void wi_setword(const char *, int, int);
+static void wi_sethex(const char *, int, char *);
+static void wi_printwords(struct wi_req *);
+static void wi_printbool(struct wi_req *);
+static void wi_printhex(struct wi_req *);
+static void wi_printaps(struct wi_req *);
+static void wi_dumpinfo(const char *);
+static void wi_dumpstats(const char *);
+static void wi_setkeys(const char *, char *, int);
+static void wi_printkeys(struct wi_req *);
+static void wi_printaplist(const char *);
+static int wi_hex2int(char);
+static void wi_str2key(char *, struct wi_key *);
+#ifdef WICACHE
+static void wi_zerocache(const char *);
+static void wi_readcache(const char *);
+#endif
+static void usage(const char *);
+static int listaps;
+static int quiet;
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ * (ripped screaming from ifconfig/ifconfig.c)
+ */
+void
+printb(char *s, uint32_t v, 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++)) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+static int
+_wi_getval(const char *iface, struct wi_req *wreq)
+{
+ struct ifreq ifr;
+ int s;
+ int retval;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)wreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1)
+ err(1, "socket");
+ retval = ioctl(s, SIOCGWAVELAN, &ifr);
+ close(s);
+
+ return (retval);
+}
+
+static int
+wi_getval(const char *iface, struct wi_req *wreq)
+{
+ if (_wi_getval(iface, wreq) == -1) {
+ if (errno != EINPROGRESS)
+ err(1, "SIOCGWAVELAN");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+wi_getvalmaybe(const char *iface, struct wi_req *wreq)
+{
+ if (_wi_getval(iface, wreq) == -1) {
+ if (errno != EINPROGRESS && errno != EINVAL)
+ err(1, "SIOCGWAVELAN");
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+wi_setval(const char *iface, struct wi_req *wreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)wreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCSWAVELAN, &ifr) == -1)
+ err(1, "SIOCSWAVELAN");
+
+ close(s);
+
+ return;
+}
+
+void
+wi_printstr(struct wi_req *wreq)
+{
+ char *ptr;
+ int i;
+
+ if (wreq->wi_type == WI_RID_SERIALNO) {
+ ptr = (char *)&wreq->wi_val;
+ for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
+ if (ptr[i] == '\0')
+ ptr[i] = ' ';
+ }
+ } else {
+ ptr = (char *)&wreq->wi_val[1];
+ for (i = 0; i < wreq->wi_val[0]; i++) {
+ if (ptr[i] == '\0')
+ ptr[i] = ' ';
+ }
+ }
+
+ ptr[i] = '\0';
+ printf("[ %s ]", ptr);
+
+ return;
+}
+
+void
+wi_setstr(const char *iface, int code, char *str)
+{
+ struct wi_req wreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ if (str == NULL)
+ errx(1, "must specify string");
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ if (strlen(str) > 30)
+ errx(1, "string too long");
+
+ wreq.wi_type = code;
+ wreq.wi_len = 18;
+ wreq.wi_val[0] = strlen(str);
+ bcopy(str, (char *)&wreq.wi_val[1], strlen(str));
+
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+void
+wi_setbytes(const char *iface, int code, char *bytes, int len)
+{
+ struct wi_req wreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_type = code;
+ wreq.wi_len = (len / 2) + 1;
+ bcopy(bytes, (char *)&wreq.wi_val[0], len);
+
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+void
+wi_setword(const char *iface, int code, int word)
+{
+ struct wi_req wreq;
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_type = code;
+ wreq.wi_len = 2;
+ wreq.wi_val[0] = word;
+
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+void
+wi_sethex(const char *iface, int code, char *str)
+{
+ struct ether_addr *addr;
+
+ if (str == NULL)
+ errx(1, "must specify address");
+
+ addr = ether_aton(str);
+
+ if (addr == NULL)
+ errx(1, "badly formatted address");
+
+ wi_setbytes(iface, code, (char *)addr, ETHER_ADDR_LEN);
+
+ return;
+}
+
+static int
+wi_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
+wi_str2key(char *s, struct wi_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->wi_keydat[0];
+ for (i = 2; s[i] != '\0' && s[i + 1] != '\0'; i+= 2) {
+ *p++ = (wi_hex2int(s[i]) << 4) + wi_hex2int(s[i + 1]);
+ n++;
+ }
+ if (s[i] != '\0')
+ errx(1, "hex strings must be of even length");
+ k->wi_keylen = n;
+ } else {
+ /* No, just copy it in. */
+ bcopy(s, k->wi_keydat, strlen(s));
+ k->wi_keylen = strlen(s);
+ }
+
+ return;
+}
+
+static void
+wi_setkeys(const char *iface, char *key, int idx)
+{
+ int keylen;
+ struct wi_req wreq;
+ struct wi_ltv_keys *keys;
+ struct wi_key *k;
+ int has_wep;
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_WEP_AVAIL;
+
+ if (wi_getval(iface, &wreq) == 0)
+ has_wep = wreq.wi_val[0];
+ else
+ has_wep = 0;
+ if (!has_wep)
+ errx(1, "no WEP option available on this card");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
+
+ if (wi_getval(iface, &wreq) == -1)
+ errx(1, "Cannot get default key index");
+ keys = (struct wi_ltv_keys *)&wreq;
+
+ keylen = strlen(key);
+ if (key[0] == '0' && (key[1] == 'x' || key[1] == 'X')) {
+ if (keylen != 2 && keylen != 12 && keylen != 28) {
+ errx(1, "encryption key must be 0, 10, or 26 "
+ "hex digits long");
+ }
+ } else {
+ if (keylen != 0 && keylen != 5 && keylen != 13) {
+ errx(1, "encryption key must be 0, 5, or 13 "
+ "bytes long");
+ }
+ }
+
+ if (idx > 3)
+ errx(1, "only 4 encryption keys available");
+
+ k = &keys->wi_keys[idx];
+ wi_str2key(key, k);
+
+ wreq.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1;
+ wreq.wi_type = WI_RID_DEFLT_CRYPT_KEYS;
+ wi_setval(iface, &wreq);
+
+ return;
+}
+
+static void
+wi_printkeys(struct wi_req *wreq)
+{
+ int i, j;
+ int isprintable;
+ struct wi_key *k;
+ struct wi_ltv_keys *keys;
+ char *ptr;
+
+ keys = (struct wi_ltv_keys *)wreq;
+
+ for (i = 0; i < 4; i++) {
+ k = &keys->wi_keys[i];
+ ptr = (char *)k->wi_keydat;
+ isprintable = 1;
+ for (j = 0; j < k->wi_keylen; j++) {
+ if (!isprint(ptr[j])) {
+ isprintable = 0;
+ break;
+ }
+ }
+ if (isprintable) {
+ ptr[j] = '\0';
+ printf("[ %s ]", ptr);
+ } else {
+ printf("[ 0x");
+ for (j = 0; j < k->wi_keylen; j++) {
+ printf("%02x", ptr[j] & 0xFF);
+ }
+ printf(" ]");
+
+ }
+ }
+
+ return;
+};
+
+void
+wi_printwords(struct wi_req *wreq)
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < wreq->wi_len - 1; i++)
+ printf("%d ", wreq->wi_val[i]);
+ printf("]");
+
+ return;
+}
+
+void
+wi_printswords(struct wi_req *wreq)
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < wreq->wi_len - 1; i++)
+ printf("%d ", ((int16_t *) wreq->wi_val)[i]);
+ printf("]");
+
+ return;
+}
+
+void
+wi_printhexwords(struct wi_req *wreq)
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < wreq->wi_len - 1; i++)
+ printf("%x ", wreq->wi_val[i]);
+ printf("]");
+
+ return;
+}
+
+void
+wi_printregdoms(struct wi_req *wreq)
+{
+ int i;
+ struct wi_ltv_domains *regdom = (struct wi_ltv_domains *)wreq;
+
+ printf("[ ");
+ for (i = 0; i < regdom->wi_num_dom; i++) {
+ switch (regdom->wi_domains[i]) {
+ case 0x10: printf("usa"); break;
+ case 0x20: printf("canada"); break;
+ case 0x30: printf("eu/au"); break;
+ case 0x31: printf("es"); break;
+ case 0x32: printf("fr"); break;
+ case 0x40: printf("jp"); break;
+ case 0x41: printf("jp new"); break;
+ default: printf("0x%x", regdom->wi_domains[i]); break;
+ }
+ if (i < regdom->wi_num_dom - 1)
+ printf(", ");
+ }
+ printf(" ]");
+
+ return;
+}
+
+void
+wi_printbool(struct wi_req *wreq)
+{
+ if (wreq->wi_val[0])
+ printf("[ On ]");
+ else
+ printf("[ Off ]");
+
+ return;
+}
+
+void
+wi_printhex(struct wi_req *wreq)
+{
+ int i;
+ unsigned char *c;
+
+ c = (unsigned char *)&wreq->wi_val;
+
+ printf("[ ");
+ for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
+ printf("%02x", c[i]);
+ if (i < ((wreq->wi_len - 1) * 2) - 1)
+ printf(":");
+ }
+
+ printf(" ]");
+ return;
+}
+
+static float
+get_wiaprate(int inrate)
+{
+ float rate;
+
+ switch (inrate) {
+ case WI_APRATE_1:
+ rate = 1;
+ break;
+ case WI_APRATE_2:
+ rate = 2;
+ break;
+ case WI_APRATE_5:
+ rate = 5.5;
+ break;
+ case WI_APRATE_11:
+ rate = 11;
+ break;
+#ifdef WI_APRATE_0
+ case WI_APRATE_0:
+#endif
+ default:
+ rate = 0;
+ break;
+ }
+
+ return (rate);
+}
+
+void
+wi_printaplist(const char *iface)
+{
+ int prism2, len, i = 0, j, r;
+ struct wi_req wreq;
+ struct wi_scan_p2_hdr *wi_p2_h;
+ struct wi_scan_res *res;
+ float rate;
+
+ if (!quiet)
+ printf("Available APs:\n");
+
+ /* first determine if this is a prism2 card or not */
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_PRISM2;
+
+ if (wi_getval(iface, &wreq) == 0)
+ prism2 = wreq.wi_val[0];
+ else
+ prism2 = 0;
+
+ /* send out a scan request */
+ wreq.wi_len = prism2 ? 3 : 1;
+ wreq.wi_type = WI_RID_SCAN_REQ;
+
+ if (prism2) {
+ wreq.wi_val[0] = 0x3FFF;
+ wreq.wi_val[1] = 0x000F;
+ }
+
+ wi_setval(iface, &wreq);
+
+ do {
+ /*
+ * sleep for 100 milliseconds so there's enough time for the card to
+ * respond... prism2's take a little longer.
+ */
+ usleep(prism2 ? 500000 : 100000);
+
+ /* get the scan results */
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_SCAN_RES;
+ } while (wi_getval(iface, &wreq) == -1 && errno == EINPROGRESS);
+
+ if (prism2) {
+ wi_p2_h = (struct wi_scan_p2_hdr *)wreq.wi_val;
+
+ /* if the reason is 0, this info is invalid */
+ if (wi_p2_h->wi_reason == 0)
+ return;
+
+ i = 4;
+ }
+
+ len = prism2 ? WI_PRISM2_RES_SIZE : WI_WAVELAN_RES_SIZE;
+
+ if (!quiet) {
+ int nstations = ((wreq.wi_len * 2) - i) / len;
+ printf("%d station%s:\n", nstations, nstations == 1 ? "" : "s");
+ printf("%-16.16s BSSID Chan SN S N Intrvl Capinfo\n", "SSID");
+ }
+ for (; i < (wreq.wi_len * 2) - len; i += len) {
+ res = (struct wi_scan_res *)((char *)wreq.wi_val + i);
+
+ res->wi_ssid[res->wi_ssid_len] = '\0';
+
+ printf("%-16.16s [ %02x:%02x:%02x:%02x:%02x:%02x ] [ %-2d ] "
+ "[ %2d %2d %2d ] %3d ", res->wi_ssid,
+ res->wi_bssid[0], res->wi_bssid[1], res->wi_bssid[2],
+ res->wi_bssid[3], res->wi_bssid[4], res->wi_bssid[5],
+ res->wi_chan, res->wi_signal - res->wi_noise,
+ res->wi_signal, res->wi_noise, res->wi_interval);
+
+ if (!quiet && res->wi_capinfo) {
+ printf("[ ");
+ if (res->wi_capinfo & WI_CAPINFO_ESS)
+ printf("ess ");
+ if (res->wi_capinfo & WI_CAPINFO_IBSS)
+ printf("ibss ");
+ if (res->wi_capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
+ printf("cfp ");
+ if (res->wi_capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
+ printf("cfpr ");
+ if (res->wi_capinfo & WI_CAPINFO_PRIV)
+ printf("priv ");
+ if (res->wi_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
+ printf("shpr ");
+ if (res->wi_capinfo & IEEE80211_CAPINFO_PBCC)
+ printf("pbcc ");
+ if (res->wi_capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
+ printf("chna ");
+ if (res->wi_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
+ printf("shst ");
+ if (res->wi_capinfo & IEEE80211_CAPINFO_DSSSOFDM)
+ printf("ofdm ");
+ printf("] ");
+ }
+
+ if (prism2 && res->wi_srates[0] != 0) {
+ printf("\n%16s [ ", "");
+ for (j = 0; j < 10 && res->wi_srates[j] != 0; j++) {
+ r = res->wi_srates[j] & IEEE80211_RATE_VAL;
+ if (r & 1)
+ printf("%d.%d", r / 2, (r % 2) * 5);
+ else
+ printf("%d", r / 2);
+ printf("%s ", res->wi_srates[j] & IEEE80211_RATE_BASIC ? "b" : "");
+ }
+ printf("] ");
+ rate = get_wiaprate(res->wi_rate);
+ if (rate)
+ printf("* %2.1f *\n", rate);
+ }
+ putchar('\n');
+ }
+}
+
+#define WI_STRING 0x01
+#define WI_BOOL 0x02
+#define WI_WORDS 0x03
+#define WI_HEXBYTES 0x04
+#define WI_KEYSTRUCT 0x05
+#define WI_SWORDS 0x06
+#define WI_HEXWORDS 0x07
+#define WI_REGDOMS 0x08
+
+struct wi_table {
+ int wi_code;
+ int wi_type;
+ const char *wi_str;
+};
+
+static struct wi_table wi_table[] = {
+ { WI_RID_SERIALNO, WI_STRING, "NIC serial number:\t\t\t" },
+ { WI_RID_NODENAME, WI_STRING, "Station name:\t\t\t\t" },
+ { WI_RID_OWN_SSID, WI_STRING, "SSID for IBSS creation:\t\t\t" },
+ { WI_RID_CURRENT_SSID, WI_STRING, "Current netname (SSID):\t\t\t" },
+ { WI_RID_DESIRED_SSID, WI_STRING, "Desired netname (SSID):\t\t\t" },
+ { WI_RID_CURRENT_BSSID, WI_HEXBYTES, "Current BSSID:\t\t\t\t" },
+ { WI_RID_CHANNEL_LIST, WI_HEXWORDS, "Channel list:\t\t\t\t" },
+ { WI_RID_OWN_CHNL, WI_WORDS, "IBSS channel:\t\t\t\t" },
+ { WI_RID_CURRENT_CHAN, WI_WORDS, "Current channel:\t\t\t" },
+ { WI_RID_COMMS_QUALITY, WI_WORDS, "Comms quality/signal/noise:\t\t" },
+ { WI_RID_DBM_COMMS_QUAL, WI_SWORDS, "dBm Coms Quality:\t\t\t" },
+ { WI_RID_PROMISC, WI_BOOL, "Promiscuous mode:\t\t\t" },
+ { WI_RID_PROCFRAME, WI_BOOL, "Process 802.11b Frame:\t\t\t" },
+ { WI_RID_PRISM2, WI_WORDS, "Intersil-Prism2 based card:\t\t" },
+ { WI_RID_PORTTYPE, WI_WORDS, "Port type (1=BSS, 3=ad-hoc):\t\t"},
+ { WI_RID_MAC_NODE, WI_HEXBYTES, "MAC address:\t\t\t\t"},
+ { WI_RID_TX_RATE, WI_WORDS, "TX rate (selection):\t\t\t"},
+ { WI_RID_CUR_TX_RATE, WI_WORDS, "TX rate (actual speed):\t\t\t"},
+ { WI_RID_RTS_THRESH, WI_WORDS, "RTS/CTS handshake threshold:\t\t"},
+ { WI_RID_CREATE_IBSS, WI_BOOL, "Create IBSS:\t\t\t\t" },
+ { WI_RID_SYSTEM_SCALE, WI_WORDS, "Access point density:\t\t\t" },
+ { WI_RID_PM_ENABLED, WI_WORDS, "Power Mgmt (1=on, 0=off):\t\t" },
+ { WI_RID_MAX_SLEEP, WI_WORDS, "Max sleep time:\t\t\t\t" },
+ { WI_RID_PRI_IDENTITY, WI_WORDS, "PRI Identity:\t\t\t\t" },
+ { WI_RID_STA_IDENTITY, WI_WORDS, "STA Identity:\t\t\t\t" } ,
+ { WI_RID_CARD_ID, WI_HEXWORDS, "Card ID register:\t\t\t" },
+ { WI_RID_REG_DOMAINS, WI_REGDOMS, "Regulatory Domains:\t\t\t" },
+ { WI_RID_TEMP_TYPE, WI_WORDS, "Temperature Range:\t\t\t" },
+#ifdef WI_EXTRA_INFO
+ { WI_RID_PRI_SUP_RANGE, WI_WORDS, "PRI Sup Range:\t\t\t\t" },
+ { WI_RID_CIF_ACT_RANGE, WI_WORDS, "CFI Act Sup Range:\t\t\t" },
+ { WI_RID_STA_SUP_RANGE, WI_WORDS, "STA Sup Range:\t\t\t\t" } ,
+ { WI_RID_MFI_ACT_RANGE, WI_WORDS, "MFI Act Sup Range:\t\t\t" } ,
+#endif
+ { 0, 0, NULL }
+};
+
+static struct wi_table wi_crypt_table[] = {
+ { WI_RID_ENCRYPTION, WI_BOOL, "WEP encryption:\t\t\t\t" },
+ { WI_RID_TX_CRYPT_KEY, WI_WORDS, "TX encryption key:\t\t\t" },
+ { WI_RID_DEFLT_CRYPT_KEYS, WI_KEYSTRUCT, "Encryption keys:\t\t\t" },
+ { 0, 0, NULL }
+};
+
+static void
+wi_dumpinfo(const char *iface)
+{
+ struct wi_req wreq;
+ int i, has_wep;
+ struct wi_table *w;
+
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_WEP_AVAIL;
+
+ if (wi_getval(iface, &wreq) == 0)
+ has_wep = wreq.wi_val[0];
+ else
+ has_wep = 0;
+
+ w = wi_table;
+
+ for (i = 0; w[i].wi_type; i++) {
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = w[i].wi_code;
+
+ if (wi_getvalmaybe(iface, &wreq) == -1)
+ continue;
+ printf("%s", w[i].wi_str);
+ switch(w[i].wi_type) {
+ case WI_STRING:
+ wi_printstr(&wreq);
+ break;
+ case WI_WORDS:
+ wi_printwords(&wreq);
+ break;
+ case WI_SWORDS:
+ wi_printswords(&wreq);
+ break;
+ case WI_HEXWORDS:
+ wi_printhexwords(&wreq);
+ break;
+ case WI_REGDOMS:
+ wi_printregdoms(&wreq);
+ break;
+ case WI_BOOL:
+ wi_printbool(&wreq);
+ break;
+ case WI_HEXBYTES:
+ wi_printhex(&wreq);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ }
+
+ if (has_wep) {
+ w = wi_crypt_table;
+ for (i = 0; w[i].wi_type; i++) {
+ bzero((char *)&wreq, sizeof(wreq));
+
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = w[i].wi_code;
+
+ if (wi_getval(iface, &wreq) == -1)
+ continue;
+ printf("%s", w[i].wi_str);
+ switch(w[i].wi_type) {
+ case WI_STRING:
+ wi_printstr(&wreq);
+ break;
+ case WI_WORDS:
+ if (wreq.wi_type == WI_RID_TX_CRYPT_KEY)
+ wreq.wi_val[0]++;
+ wi_printwords(&wreq);
+ break;
+ case WI_BOOL:
+ wi_printbool(&wreq);
+ break;
+ case WI_HEXBYTES:
+ wi_printhex(&wreq);
+ break;
+ case WI_KEYSTRUCT:
+ wi_printkeys(&wreq);
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ }
+ }
+
+ if (listaps)
+ wi_printaplist(iface);
+
+ return;
+}
+
+static void
+wi_dumpstats(const char *iface)
+{
+ struct wi_req wreq;
+ struct wi_counters *c;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_IFACE_STATS;
+
+ if (wi_getval(iface, &wreq) == -1)
+ errx(1, "Cannot get interface stats");
+
+ c = (struct wi_counters *)&wreq.wi_val;
+
+ printf("Transmitted unicast frames:\t\t%d\n",
+ c->wi_tx_unicast_frames);
+ printf("Transmitted multicast frames:\t\t%d\n",
+ c->wi_tx_multicast_frames);
+ printf("Transmitted fragments:\t\t\t%d\n",
+ c->wi_tx_fragments);
+ printf("Transmitted unicast octets:\t\t%d\n",
+ c->wi_tx_unicast_octets);
+ printf("Transmitted multicast octets:\t\t%d\n",
+ c->wi_tx_multicast_octets);
+ printf("Single transmit retries:\t\t%d\n",
+ c->wi_tx_single_retries);
+ printf("Multiple transmit retries:\t\t%d\n",
+ c->wi_tx_multi_retries);
+ printf("Transmit retry limit exceeded:\t\t%d\n",
+ c->wi_tx_retry_limit);
+ printf("Transmit discards:\t\t\t%d\n",
+ c->wi_tx_discards);
+ printf("Transmit discards due to wrong SA:\t%d\n",
+ c->wi_tx_discards_wrong_sa);
+ printf("Received unicast frames:\t\t%d\n",
+ c->wi_rx_unicast_frames);
+ printf("Received multicast frames:\t\t%d\n",
+ c->wi_rx_multicast_frames);
+ printf("Received fragments:\t\t\t%d\n",
+ c->wi_rx_fragments);
+ printf("Received unicast octets:\t\t%d\n",
+ c->wi_rx_unicast_octets);
+ printf("Received multicast octets:\t\t%d\n",
+ c->wi_rx_multicast_octets);
+ printf("Receive FCS errors:\t\t\t%d\n",
+ c->wi_rx_fcs_errors);
+ printf("Receive discards due to no buffer:\t%d\n",
+ c->wi_rx_discards_nobuf);
+ printf("Can't decrypt WEP frame:\t\t%d\n",
+ c->wi_rx_WEP_cant_decrypt);
+ printf("Received message fragments:\t\t%d\n",
+ c->wi_rx_msg_in_msg_frags);
+ printf("Received message bad fragments:\t\t%d\n",
+ c->wi_rx_msg_in_bad_msg_frags);
+
+ return;
+}
+
+static void
+usage(const 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 -l\n", p);
+ fprintf(stderr, "\t%s -i iface -L\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 -s station name\n", p);
+ fprintf(stderr, "\t%s -i iface -c 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -q SSID\n", p);
+ fprintf(stderr, "\t%s -i iface -p port type\n", p);
+ fprintf(stderr, "\t%s -i iface -a access point density\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 -e 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -k encryption key [-v 1|2|3|4]\n", p);
+ fprintf(stderr, "\t%s -i iface -r RTS threshold\n", p);
+ fprintf(stderr, "\t%s -i iface -f frequency\n", p);
+ fprintf(stderr, "\t%s -i iface -F 0|1\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 -T 1|2|3|4\n", p);
+#ifdef WICACHE
+ fprintf(stderr, "\t%s -i iface -Z zero out signal cache\n", p);
+ fprintf(stderr, "\t%s -i iface -C print signal cache\n", p);
+#endif
+
+ exit(1);
+}
+
+static void
+wi_printaps(struct wi_req *wreq)
+{
+ struct wi_apinfo *w;
+ int i, j, nstations, rate;
+
+ nstations = *(int *)wreq->wi_val;
+ printf("%d station%s:\n", nstations, nstations == 1 ? "" : "s");
+ w = (struct wi_apinfo *)(((char *)&wreq->wi_val) + sizeof(int));
+ for ( i = 0; i < nstations; i++, w++) {
+ printf("ap[%d]:\n", i);
+ if (w->scanreason) {
+ static char *scanm[] = {
+ "Host initiated",
+ "Firmware initiated",
+ "Inquiry request from host"
+ };
+ printf("\tScanReason:\t\t\t[ %s ]\n",
+ scanm[w->scanreason - 1]);
+ }
+ printf("\tnetname (SSID):\t\t\t[ ");
+ for (j = 0; j < w->namelen; j++) {
+ printf("%c", w->name[j]);
+ }
+ printf(" ]\n");
+ printf("\tBSSID:\t\t\t\t[ %02x:%02x:%02x:%02x:%02x:%02x ]\n",
+ w->bssid[0]&0xff, w->bssid[1]&0xff,
+ w->bssid[2]&0xff, w->bssid[3]&0xff,
+ w->bssid[4]&0xff, w->bssid[5]&0xff);
+ printf("\tChannel:\t\t\t[ %d ]\n", w->channel);
+ printf("\tQuality/Signal/Noise [signal]:\t[ %d / %d / %d ]\n"
+ "\t [dBm]:\t[ %d / %d / %d ]\n",
+ w->quality, w->signal, w->noise,
+ w->quality, w->signal - 149, w->noise - 149);
+ printf("\tBSS Beacon Interval [msec]:\t[ %d ]\n", w->interval);
+ printf("\tCapinfo:\t\t\t[ ");
+ if (w->capinfo & IEEE80211_CAPINFO_ESS)
+ printf("ESS ");
+ if (w->capinfo & IEEE80211_CAPINFO_PRIVACY)
+ printf("WEP ");
+ printf("]\n");
+
+ switch (w->rate) {
+ case WI_APRATE_1:
+ rate = 1;
+ break;
+ case WI_APRATE_2:
+ rate = 2;
+ break;
+ case WI_APRATE_5:
+ rate = 5.5;
+ break;
+ case WI_APRATE_11:
+ rate = 11;
+ break;
+#ifdef WI_APRATE_0
+ case WI_APRATE_0:
+#endif
+ default:
+ rate = 0;
+ break;
+ }
+ if (rate) printf("\tDataRate [Mbps]:\t\t[ %d ]\n", rate);
+ }
+}
+
+static void
+wi_dumpstations(const char *iface)
+{
+ struct wi_req wreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_READ_APS;
+
+ if (wi_getval(iface, &wreq) == -1)
+ errx(1, "Cannot get stations");
+ wi_printaps(&wreq);
+}
+
+#ifdef WICACHE
+static void
+wi_zerocache(const char *iface)
+{
+ struct wi_req wreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = 0;
+ wreq.wi_type = WI_RID_ZERO_CACHE;
+ wi_getval(iface, &wreq);
+}
+
+static void
+wi_readcache(const char *iface)
+{
+ struct wi_req wreq;
+ int *wi_sigitems;
+ struct wi_sigcache *sc;
+ char * pt;
+ int i;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&wreq, sizeof(wreq));
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_READ_CACHE;
+ if (wi_getval(iface, &wreq) == -1)
+ errx(1, "Cannot read signal cache");
+
+ wi_sigitems = (int *) &wreq.wi_val;
+ pt = ((char *) &wreq.wi_val);
+ pt += sizeof(int);
+ sc = (struct wi_sigcache *) pt;
+
+ for (i = 0; i < *wi_sigitems; i++) {
+ printf("[%d/%d]:", i+1, *wi_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++;
+ }
+
+ return;
+}
+#endif
+
+static void
+dep(const char *flag, const char *opt)
+{
+ warnx("warning: flag %s deprecated, migrate to ifconfig %s", flag,
+ opt);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ const char *iface = NULL;
+ char *p = argv[0];
+ char *key = NULL;
+ int modifier = 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 = "wi0";
+ optind = 1;
+ }
+ optreset = 1;
+ }
+ opterr = 1;
+
+ while((ch = getopt(argc, argv,
+ "a:c:d:e:f:hi:k:lm:n:op:q:r:s:t:v:CF:LP:QS:T:Z")) != -1) {
+ switch(ch) {
+ case 'Z':
+#ifdef WICACHE
+ wi_zerocache(iface);
+#else
+ printf("WICACHE not available\n");
+#endif
+ exit(0);
+ break;
+ case 'C':
+#ifdef WICACHE
+ wi_readcache(iface);
+#else
+ printf("WICACHE not available\n");
+#endif
+ exit(0);
+ break;
+ case 'o':
+ wi_dumpstats(iface);
+ exit(0);
+ break;
+ case 'c':
+ dep("c", "mediaopt");
+ wi_setword(iface, WI_RID_CREATE_IBSS, atoi(optarg));
+ exit(0);
+ break;
+ case 'd':
+ wi_setword(iface, WI_RID_MAX_DATALEN, atoi(optarg));
+ exit(0);
+ break;
+ case 'e':
+ dep("e", "wepmode");
+ wi_setword(iface, WI_RID_ENCRYPTION, atoi(optarg));
+ exit(0);
+ break;
+ case 'f':
+ dep("f", "channel");
+ wi_setword(iface, WI_RID_OWN_CHNL, atoi(optarg));
+ exit(0);
+ break;
+ case 'F':
+ wi_setword(iface, WI_RID_PROCFRAME, atoi(optarg));
+ exit(0);
+ break;
+ case 'k':
+ dep("k", "wepkey");
+ key = optarg;
+ break;
+ case 'L':
+ listaps++;
+ break;
+ case 'l':
+ wi_dumpstations(iface);
+ exit(0);
+ break;
+ case 'p':
+ dep("p", "mediaopt");
+ wi_setword(iface, WI_RID_PORTTYPE, atoi(optarg));
+ exit(0);
+ break;
+ case 'r':
+ wi_setword(iface, WI_RID_RTS_THRESH, atoi(optarg));
+ exit(0);
+ break;
+ case 't':
+ dep("t", "mediaopt");
+ wi_setword(iface, WI_RID_TX_RATE, atoi(optarg));
+ exit(0);
+ break;
+ case 'n':
+ dep("n", "ssid");
+ wi_setstr(iface, WI_RID_DESIRED_SSID, optarg);
+ exit(0);
+ break;
+ case 's':
+ dep("s", "stationname");
+ wi_setstr(iface, WI_RID_NODENAME, optarg);
+ exit(0);
+ break;
+ case 'm':
+ wi_sethex(iface, WI_RID_MAC_NODE, optarg);
+ exit(0);
+ break;
+ case 'Q':
+ quiet = 1;
+ break;
+ case 'q':
+ dep("q", "ssid");
+ wi_setstr(iface, WI_RID_OWN_SSID, optarg);
+ exit(0);
+ break;
+ case 'S':
+ dep("S", "powersleep");
+ wi_setword(iface, WI_RID_MAX_SLEEP, atoi(optarg));
+ exit(0);
+ break;
+ case 'T':
+ dep("T", "weptxkey");
+ wi_setword(iface,
+ WI_RID_TX_CRYPT_KEY, atoi(optarg) - 1);
+ exit(0);
+ break;
+ case 'P':
+ dep("P", "powersave");
+ wi_setword(iface, WI_RID_PM_ENABLED, atoi(optarg));
+ exit(0);
+ break;
+ case 'a':
+ wi_setword(iface, WI_RID_SYSTEM_SCALE, atoi(optarg));
+ exit(0);
+ break;
+ case 'v':
+ modifier = atoi(optarg);
+ modifier--;
+ break;
+ case 'h':
+ default:
+ usage(p);
+ break;
+ }
+ }
+
+ if (iface == NULL)
+ usage(p);
+
+ if (key != NULL) {
+ wi_setkeys(iface, key, modifier);
+ exit(0);
+ }
+
+ if (listaps > 1) {
+ wi_printaplist(iface);
+ exit(0);
+ }
+
+ wi_dumpinfo(iface);
+
+ exit(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..f63b691
--- /dev/null
+++ b/usr.sbin/wlconfig/wlconfig.8
@@ -0,0 +1,138 @@
+.\" $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/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..f7b8f22
--- /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 couldn't 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..1806c3c
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.c
@@ -0,0 +1,328 @@
+/*
+ * 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"
+
+struct cu_data {
+ int cu_fd; /* connections fd */
+ bool_t cu_closeit; /* opened by library */
+ struct sockaddr_storage cu_raddr; /* remote address */
+ int cu_rlen;
+ struct timeval cu_wait; /* retransmit interval */
+ struct timeval cu_total; /* total time for the call */
+ struct rpc_err cu_error;
+ XDR cu_outxdrs;
+ u_int cu_xdrpos;
+ u_int cu_sendsz; /* send size */
+ char *cu_outbuf;
+ u_int cu_recvsz; /* recv size */
+ struct pollfd pfdp;
+ int cu_async;
+ char cu_inbuf[1];
+};
+
+/*
+ * 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;
+ int winner = -1;
+ u_int32_t xid_seed, xid_lookup;
+ int sock, dontblock = 1;
+ CLIENT *clnt;
+ char *foo = dom;
+ struct cu_data *cu;
+ 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();
+ cu = (struct cu_data *)clnt->cl_private;
+ 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);
+ bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr,
+ sizeof(struct sockaddr_in));
+ 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..080afa7
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.8
@@ -0,0 +1,205 @@
+.\" 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.
+.\" 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 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
+can't 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..bec3b9e
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.c
@@ -0,0 +1,991 @@
+/*
+ * 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>
+struct dom_binding{};
+#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..ca5e483
--- /dev/null
+++ b/usr.sbin/yppush/yppush.8
@@ -0,0 +1,181 @@
+.\" 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.
+.\" 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 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 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.
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
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..e3ef548
--- /dev/null
+++ b/usr.sbin/yppush/yppush_main.c
@@ -0,0 +1,684 @@
+/*
+ * 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 <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>
+struct dom_binding {};
+#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 verbose = 0; /* Toggle verbose mode. */
+unsigned long yppush_transid = 0;
+int yppush_timeout = 80; /* Default timeout. */
+int yppush_jobs = 0; /* Number of allowed concurrent jobs. */
+int yppush_running_jobs = 0; /* Number of currently running jobs. */
+int yppush_alarm_tripped = 0;
+
+/* Structure for holding information about a running job. */
+struct jobs {
+ unsigned long tid;
+ int sock;
+ 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. */
+
+/*
+ * 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) {
+ if (job->tid == tid)
+ break;
+ job = job->next;
+ }
+
+ if (job->polled) {
+ 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 " : " ");
+ yppush_alarm_tripped = 0;
+ alarm(YPPUSH_RESPONSE_TIMEOUT);
+ pause();
+ alarm(0);
+ if (yppush_alarm_tripped == 1) {
+ 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)
+{
+ if (sig == SIGTERM || sig == SIGINT || sig == SIGABRT) {
+ yppush_jobs = 0;
+ yppush_exit(1);
+ }
+
+ if (sig == SIGALRM) {
+ alarm(0);
+ yppush_alarm_tripped++;
+ }
+
+ return;
+}
+
+/*
+ * Dispatch loop for callback RPC services.
+ */
+static void
+yppush_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 5;
+
+retry:
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(_rpc_dtablesize(), &readfds, NULL, NULL, &timeout)) {
+ 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;
+}
+
+/*
+ * Special handler for asynchronous socket I/O. We mark the
+ * sockets of the callback handlers as O_ASYNC and handle SIGIO
+ * events here, which will occur when the callback handler has
+ * something interesting to tell us.
+ */
+static void
+async_handler(int sig)
+{
+ yppush_svc_run();
+
+ /* reset any pending alarms. */
+ alarm(0);
+ yppush_alarm_tripped++;
+ kill(getpid(), SIGALRM);
+ return;
+}
+
+/*
+ * 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 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;
+ }
+
+ /* Register the job in our linked list of jobs. */
+ if ((job = (struct jobs *)malloc(sizeof (struct jobs))) == NULL) {
+ yp_error("malloc failed");
+ yppush_exit(1);
+ }
+
+ /* Initialize the info for this job. */
+ job->stat = 0;
+ job->tid = tid;
+ job->port = xprt->xp_port;
+ job->sock = xprt->xp_fd; /*XXX: Evil!! EEEEEEEVIL!!! */
+ job->server = strdup(server);
+ job->map = strdup(map);
+ job->prognum = prognum;
+ job->polled = 0;
+ job->next = yppush_joblist;
+ yppush_joblist = job;
+
+ /*
+ * Set the RPC sockets to asynchronous mode. This will
+ * cause the system to smack us with a SIGIO when an RPC
+ * callback is delivered. This in turn allows us to handle
+ * the callback even though we may be in the middle of doing
+ * something else at the time.
+ *
+ * XXX This is a horrible thing to do for two reasons,
+ * both of which have to do with portability:
+ * 1) We really ought not to be sticking our grubby mits
+ * into the RPC service transport handle like this.
+ * 2) Even in this day and age, there are still some *NIXes
+ * that don't support async socket I/O.
+ */
+ if (fcntl(xprt->xp_fd, F_SETOWN, getpid()) == -1 ||
+ fcntl(xprt->xp_fd, F_SETFL, O_ASYNC) == -1) {
+ yp_error("failed to set async I/O mode: %s",
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ 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 can 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);
+
+ /*
+ * 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.
+ */
+ if (yppush_jobs <= 1) {
+ yppush_alarm_tripped = 0;
+ while (!yppush_alarm_tripped && yppush_running_jobs) {
+ alarm(yppush_timeout);
+ yppush_alarm_tripped = 0;
+ pause();
+ alarm(0);
+ }
+ } else {
+ yppush_alarm_tripped = 0;
+ while (!yppush_alarm_tripped && yppush_running_jobs >= yppush_jobs) {
+ alarm(yppush_timeout);
+ yppush_alarm_tripped = 0;
+ pause();
+ alarm(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 (strncmp(myname, data.data, data.size)) {
+ 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(SIGALRM, handler);
+ signal(SIGTERM, handler);
+ signal(SIGINT, handler);
+ signal(SIGABRT, handler);
+
+ /*
+ * Set up the SIGIO handler. Make sure that some of the
+ * other signals are blocked while the handler is running so
+ * select() doesn't get interrupted.
+ */
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGIO); /* Goes without saying. */
+ sigaddset(&sa.sa_mask, SIGPIPE);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGINT);
+ sa.sa_handler = async_handler;
+ sa.sa_flags = 0;
+
+ sigaction(SIGIO, &sa, NULL);
+
+ /* 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..db21523
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile
@@ -0,0 +1,43 @@
+# $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
+
+afterinstall:
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${.CURDIR}/Makefile.yp \
+ ${DESTDIR}/var/yp/Makefile.dist
+ ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 555 \
+ ${.CURDIR}/ypinit.sh ${DESTDIR}${BINDIR}/ypinit
+ @if [ ! -f ${DESTDIR}/var/yp/Makefile.dist ]; then \
+ ln -s ${DESTDIR}/var/yp/Makefile.dist \
+ ${DESTDIR}/var/yp/Makefile; fi
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypserv/Makefile.yp b/usr.sbin/ypserv/Makefile.yp
new file mode 100644
index 0000000..bd33918
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile.yp
@@ -0,0 +1,577 @@
+#
+# 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
+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
+
+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
+networks: networks.byaddr networks.byname
+protocols: protocols.bynumber protocols.byname
+rpc: rpc.byname rpc.bynumber
+services: services.byname
+shells: shells.list
+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
+ @$(MAKE) -f ../Makefile netid
+.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
+ @$(MAKE) -f ../Makefile netid
+
+
+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
+ @$(MAKE) -f ../Makefile netid
+
+
+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.list: $(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
+ @$(MAKE) -f ../Makefile netid
+
+
+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
+ @$(MAKE) -f ../Makefile netid
+
+
+netid.byname: $(GROUP) $(PASSWD)
+ @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..b906309
--- /dev/null
+++ b/usr.sbin/ypserv/yp_access.c
@@ -0,0 +1,323 @@
+/*
+ * 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"
+};
+
+#ifdef TCP_WRAPPER
+void
+load_securenets(void)
+{
+}
+#else
+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);
+
+}
+#endif
+
+/*
+ * 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 = 0;
+ static unsigned long oldaddr = 0;
+#ifndef TCP_WRAPPER
+ struct securenet *tmp;
+#endif
+ 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 = hosts_ctl("ypserv", STRING_UNKNOWN,
+ inet_ntoa(rqhost->sin_addr), "");
+#else
+ tmp = securenets;
+ while (tmp) {
+ if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr)
+ | tmp->net.s_addr) == rqhost->sin_addr.s_addr) {
+ status = 1;
+ break;
+ }
+ tmp = tmp->next;
+ }
+#endif
+
+ if (!status) {
+ 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..50270fc
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dnslookup.c
@@ -0,0 +1,522 @@
+/*
+ * 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 len,i;
+ struct in_addr addr;
+
+ if (hp == NULL)
+ return(NULL);
+
+ len = 16 + 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));
+
+ bcopy(hp->h_addr, &addr, sizeof(struct in_addr));
+ snprintf(result, sizeof(result), "%s %s", inet_ntoa(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 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;
+ struct in_addr addr;
+ 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];
+ char retrybuf[MAXHOSTNAMELEN];
+ struct sockaddr_in sin;
+ int rval;
+ int len;
+ 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.s_addr;
+ hent->h_length = sizeof(struct in_addr);
+ }
+ }
+
+ /* 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)
+{
+ register struct circleq_dnsentry *q;
+ int type, len;
+
+ /* 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 = T_A;
+ 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)
+{
+ register struct circleq_dnsentry *q;
+ char buf[MAXHOSTNAMELEN];
+ int a, b, c, d;
+ int type, len;
+
+ /* 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);
+
+ if (sscanf(addr, "%d.%d.%d.%d", &a, &b, &c, &d) != 4)
+ return(YP_NOKEY);
+
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", d, c, b, a);
+
+ 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);
+ }
+
+ inet_aton(addr, &q->addr);
+ 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..553e296
--- /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 *);
+extern ypstat yp_async_lookup_addr(struct svc_req *, char *);
diff --git a/usr.sbin/ypserv/yp_main.c b/usr.sbin/ypserv/yp_main.c
new file mode 100644
index 0000000..d913274
--- /dev/null
+++ b/usr.sbin/ypserv/yp_main.c
@@ -0,0 +1,338 @@
+/*
+ * 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;
+
+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 */
+ extern int forked;
+ 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]\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;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "hdnp:")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = ypdb_debug = 1;
+ break;
+ case 'n':
+ do_dns = 1;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ 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);
+ }
+
+ 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..e6d82d6
--- /dev/null
+++ b/usr.sbin/ypserv/yp_server.c
@@ -0,0 +1,973 @@
+/*
+ * 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 (result.stat != YP_TRUE &&
+ (yp_testflag(argp->map, argp->domain, YP_INTERDOMAIN) ||
+ (strstr(argp->map, "hosts") && do_dns))) {
+#else
+ if (do_dns && result.stat != YP_TRUE && strstr(argp->map, "hosts")) {
+#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);
+ else if (!strcmp(argp->map, "hosts.byaddr"))
+ result.stat = yp_async_lookup_addr(rqstp, nbuf);
+
+ 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..a3a7a8d
--- /dev/null
+++ b/usr.sbin/ypserv/ypinit.8
@@ -0,0 +1,196 @@
+.\" 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 /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 yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8 ,
+.Xr yp_mkdb 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..f517302
--- /dev/null
+++ b/usr.sbin/ypserv/ypserv.8
@@ -0,0 +1,447 @@
+.\" 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 4, 1995
+.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 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 doesn't hurt and it's 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 aren't 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
+can't 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 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 AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.2 .
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..1296f39
--- /dev/null
+++ b/usr.sbin/ypset/ypset.c
@@ -0,0 +1,151 @@
+/*
+ * 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>
+struct dom_binding{};
+#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", 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
index 39dcab7..baa84ed 100644
--- a/usr.sbin/zic/Arts.htm
+++ b/usr.sbin/zic/Arts.htm
@@ -1,4 +1,5 @@
<!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>
@@ -143,7 +144,7 @@ Copyright Date: 1996
Label: Black Lion
ID: BLCD 760221
Total Time: 72:58
-Personnel: Anthony Braxton, sporanino and alto saxophones,
+Personnel: Anthony Braxton, sopranino and alto saxophones,
contrebasse clarinet, miscellaneous instruments
Leo Smith, trumpet and miscellaneous instruments
Leroy Jenkins, violin and miscellaneous instruments
diff --git a/usr.sbin/zic/Makefile b/usr.sbin/zic/Makefile
index 0aefc95..2496e82 100644
--- a/usr.sbin/zic/Makefile
+++ b/usr.sbin/zic/Makefile
@@ -1,365 +1,8 @@
-# @(#)Makefile 7.67
+# $FreeBSD$
-# Change the line below for your time zone (after finding the zone you want in
-# the time zone files, or adding it to a time zone file).
-# Alternately, if you discover you've got the wrong time zone, you can just
-# zic -l rightzone
-# to correct things.
-# Use the command
-# make zonenames
-# to get a list of the values you can use for LOCALTIME.
+# Vendor contact: tz@elsie.nci.nih.gov
+MAINTAINER= wollman@FreeBSD.org
-LOCALTIME= Factory
+SUBDIR= zic zdump
-# If you want something other than Eastern United States time as a template
-# for handling POSIX-style time zone environment variables,
-# change the line below (after finding the zone you want in the
-# time zone files, or adding it to a time zone file).
-# (When a POSIX-style environment variable is handled, the rules in the
-# template file are used to determine "spring forward" and "fall back" days and
-# times; the environment variable itself specifies UTC offsets of standard and
-# summer time.)
-# Alternately, if you discover you've got the wrong time zone, you can just
-# zic -p rightzone
-# to correct things.
-# Use the command
-# make zonenames
-# to get a list of the values you can use for POSIXRULES.
-# If you want POSIX compatibility, use "America/New_York".
-
-POSIXRULES= America/New_York
-
-# Everything gets put in subdirectories of. . .
-
-TOPDIR= /usr/local
-
-# "Compiled" time zone information is placed in the "TZDIR" directory
-# (and subdirectories).
-# Use an absolute path name for TZDIR unless you're just testing the software.
-
-TZDIR= $(TOPDIR)/etc/zoneinfo
-
-# The "zic" and "zdump" commands get installed in. . .
-
-ETCDIR= $(TOPDIR)/etc
-
-# If you "make INSTALL", the "date" command gets installed in. . .
-
-BINDIR= $(TOPDIR)/bin
-
-# Manual pages go in subdirectories of. . .
-
-MANDIR= $(TOPDIR)/man
-
-# Library functions are put in an archive in LIBDIR.
-
-LIBDIR= $(TOPDIR)/lib
-TZLIB= $(LIBDIR)/libtz.a
-
-# If you always want time values interpreted as "seconds since the epoch
-# (not counting leap seconds)", use
-# REDO= posix_only
-# below. If you always want right time values interpreted as "seconds since
-# the epoch" (counting leap seconds)", use
-# REDO= right_only
-# below. If you want both sets of data available, with leap seconds not
-# counted normally, use
-# REDO= posix_right
-# below. If you want both sets of data available, with leap seconds counted
-# normally, use
-# REDO= right_posix
-# below.
-# POSIX mandates that leap seconds not be counted; for compatibility with it,
-# use either "posix_only" or "posix_right".
-
-REDO= posix_right
-
-# Since "." may not be in PATH...
-
-YEARISTYPE= ./yearistype
-
-# Non-default libraries needed to link.
-# Add -lintl if you want to use `gettext' on Solaris.
-LDLIBS=
-
-# Add the following to the end of the "CFLAGS=" line as needed.
-# -DHAVE_ADJTIME=0 if `adjtime' does not exist (SVR0?)
-# -DHAVE_GETTEXT=1 if `gettext' works (GNU, Linux, Solaris); also see LDLIBS
-# -DHAVE_LONG_DOUBLE=1 if your compiler supports the `long double' type
-# -DHAVE_SETTIMEOFDAY=0 if settimeofday does not exist (SVR0?)
-# -DHAVE_SETTIMEOFDAY=1 if settimeofday has just 1 arg (SVR4)
-# -DHAVE_SETTIMEOFDAY=2 if settimeofday uses 2nd arg (4.3BSD)
-# -DHAVE_SETTIMEOFDAY=3 if settimeofday ignores 2nd arg (4.4BSD)
-# -DHAVE_STRERROR=1 if `strerror' works
-# -DHAVE_SYMLINK=0 if your system lacks the symlink function
-# -DLOCALE_HOME=\"path\" if locales are in "path", not "/usr/lib/locale"
-# -DHAVE_UNISTD_H=0 if your compiler lacks a "unistd.h" (Microsoft C++ 7?)
-# -DHAVE_UTMPX_H=1 if your compiler has a "utmpx.h"
-# -DTZ_DOMAIN=\"foo\" to use "foo" for gettext domain name; default is "tz"
-# -TTZ_DOMAINDIR=\"/path\" to use "/path" for gettext directory;
-# the default is system-supplied, typically "/usr/lib/locale"
-# $(GCC_DEBUG_FLAGS) if you are using GCC and want lots of checking
-# -DNO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU=1
-# if you do not want run time warnings about formats that may cause
-# year 2000 grief
-#
-GCC_DEBUG_FLAGS = -Dlint -g -O -fno-common \
- -Wall -Wcast-qual -Wconversion -Wmissing-prototypes \
- -Wnested-externs -Wpointer-arith -Wshadow \
- -Wtraditional # -Wstrict-prototypes -Wwrite-strings
-#
-# If you want to use System V compatibility code, add
-# -DUSG_COMPAT
-# to the end of the "CFLAGS=" line. This arrange for "timezone" and "daylight"
-# variables to be kept up-to-date by the time conversion functions. Neither
-# "timezone" nor "daylight" is described in X3J11's work.
-#
-# If your system has a "GMT offset" field in its "struct tm"s
-# (or if you decide to add such a field in your system's "time.h" file),
-# add the name to a define such as
-# -DTM_GMTOFF=tm_gmtoff
-# or
-# -DTM_GMTOFF=_tm_gmtoff
-# to the end of the "CFLAGS=" line.
-# Neither tm_gmtoff nor _tm_gmtoff is described in X3J11's work;
-# in its work, use of "tm_gmtoff" is described as non-conforming.
-# Both Linux and BSD have done the equivalent of defining TM_GMTOFF in
-# their recent releases.
-#
-# If your system has a "zone abbreviation" field in its "struct tm"s
-# (or if you decide to add such a field in your system's "time.h" file),
-# add the name to a define such as
-# -DTM_ZONE=tm_zone
-# or
-# -DTM_ZONE=_tm_zone
-# to the end of the "CFLAGS=" line.
-# Neither tm_zone nor _tm_zone is described in X3J11's work;
-# in its work, use of "tm_zone" is described as non-conforming.
-# Both UCB and Sun have done the equivalent of defining TM_ZONE in
-# their recent releases.
-#
-# If you want functions that were inspired by early versions of X3J11's work,
-# add
-# -DSTD_INSPIRED
-# to the end of the "CFLAGS=" line. This arranges for the functions
-# "tzsetwall", "offtime", "timelocal", "timegm", "timeoff",
-# "posix2time", and "time2posix" to be added to the time conversion library.
-# "tzsetwall" is like "tzset" except that it arranges for local wall clock
-# time (rather than the time specified in the TZ environment variable)
-# to be used.
-# "offtime" is like "gmtime" except that it accepts a second (long) argument
-# that gives an offset to add to the time_t when converting it.
-# "timelocal" is equivalent to "mktime".
-# "timegm" is like "timelocal" except that it turns a struct tm into
-# a time_t using UTC (rather than local time as "timelocal" does).
-# "timeoff" is like "timegm" except that it accepts a second (long) argument
-# that gives an offset to use when converting to a time_t.
-# "posix2time" and "time2posix" are described in an included manual page.
-# None of these functions are described in X3J11's current work.
-# Sun has provided "tzsetwall", "timelocal", and "timegm" in SunOS 4.0.
-# These functions may well disappear in future releases of the time
-# conversion package.
-#
-# If you want Source Code Control System ID's left out of object modules, add
-# -DNOID
-# to the end of the "CFLAGS=" line.
-#
-# If you'll never want to handle solar-time-based time zones, add
-# -DNOSOLAR
-# to the end of the "CFLAGS=" line
-# (and comment out the "SDATA=" line below).
-# This reduces (slightly) the run-time data-space requirements of
-# the time conversion functions; it may reduce the acceptability of your system
-# to folks in oil- and cash-rich places.
-#
-# If you want to allocate state structures in localtime, add
-# -DALL_STATE
-# to the end of the "CFLAGS=" line. Storage is obtained by calling malloc.
-#
-# If you want an "altzone" variable (a la System V Release 3.1), add
-# -DALTZONE
-# to the end of the "CFLAGS=" line.
-# This variable is not described in X3J11's work.
-#
-# If you want a "gtime" function (a la MACH), add
-# -DCMUCS
-# to the end of the "CFLAGS=" line
-# This function is not described in X3J11's work.
-#
-# NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put
-# out by the National Institute of Standards and Technology
-# which claims to test C and Posix conformance. If you want to pass PCTS, add
-# -DPCTS
-# to the end of the "CFLAGS=" line.
-#
-# If you want strict compliance with XPG4 as of 1994-04-09, add
-# -DXPG4_1994_04_09
-# to the end of the "CFLAGS=" line. This causes "strftime" to always return
-# 53 as a week number (rather than 52 or 53) for those days in January that
-# before the first Monday in January when a "%V" format is used and January 1
-# falls on a Friday, Saturday, or Sunday.
-
-CFLAGS=
-
-# If you want zic's -s option used when installing, uncomment the next line
-# ZFLAGS= -s
-
-zic= ./zic
-ZIC= $(zic) $(ZFLAGS)
-
-# The name of a Posix-compliant `awk' on your system.
-AWK= awk
-
-###############################################################################
-
-cc= cc
-CC= $(cc) -DTZDIR=\"$(TZDIR)\"
-
-TZCSRCS= zic.c localtime.c asctime.c scheck.c ialloc.c
-TZCOBJS= zic.o localtime.o asctime.o scheck.o ialloc.o
-TZDSRCS= zdump.c localtime.c asctime.c ialloc.c
-TZDOBJS= zdump.o localtime.o asctime.o ialloc.o
-DATESRCS= date.c localtime.c logwtmp.c strftime.c asctime.c
-DATEOBJS= date.o localtime.o logwtmp.o strftime.o asctime.o
-LIBSRCS= localtime.c asctime.c difftime.c
-LIBOBJS= localtime.o asctime.o difftime.o
-HEADERS= tzfile.h private.h
-NONLIBSRCS= zic.c zdump.c scheck.c ialloc.c
-NEWUCBSRCS= date.c logwtmp.c strftime.c
-SOURCES= $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) tzselect.ksh
-MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
- tzfile.5 tzselect.8 zic.8 zdump.8
-DOCS= README Theory $(MANS) date.1 Makefile
-PRIMARY_YDATA= africa antarctica asia australasia \
- europe northamerica southamerica
-YDATA= $(PRIMARY_YDATA) pacificnew etcetera factory backward
-NDATA= systemv
-SDATA= solar87 solar88 solar89
-TDATA= $(YDATA) $(NDATA) $(SDATA)
-TABDATA= iso3166.tab zone.tab
-DATA= $(YDATA) $(NDATA) $(SDATA) $(TABDATA) leapseconds yearistype.sh
-MISC= usno1988 usno1989 usno1989a usno1995 usno1997 usno1998 \
- Arts.htm WWW.htm gccdiffs checktab.awk
-ENCHILADA= $(DOCS) $(SOURCES) $(DATA) $(MISC)
-
-# And for the benefit of csh users on systems that assume the user
-# shell should be used to handle commands in Makefiles. . .
-
-SHELL= /bin/sh
-
-all: zic zdump $(LIBOBJS)
-
-ALL: all date tzselect
-
-install: all $(DATA) $(REDO) $(TZLIB) $(MANS) $(TABDATA)
- $(ZIC) -y $(YEARISTYPE) \
- -d $(TZDIR) -l $(LOCALTIME) -p $(POSIXRULES)
- -rm -f $(TZDIR)/iso3166.tab $(TZDIR)/zone.tab
- cp iso3166.tab zone.tab $(TZDIR)/.
- -mkdir $(TOPDIR) $(ETCDIR)
- cp zic zdump $(ETCDIR)/.
- -mkdir $(TOPDIR) $(MANDIR) \
- $(MANDIR)/man3 $(MANDIR)/man5 $(MANDIR)/man8
- -rm -f $(MANDIR)/man3/newctime.3 \
- $(MANDIR)/man3/newtzset.3 \
- $(MANDIR)/man5/tzfile.5 \
- $(MANDIR)/man8/tzselect.8 \
- $(MANDIR)/man8/zdump.8 \
- $(MANDIR)/man8/zic.8
- cp newctime.3 newtzset.3 $(MANDIR)/man3/.
- cp tzfile.5 $(MANDIR)/man5/.
- cp tzselect.8 zdump.8 zic.8 $(MANDIR)/man8/.
-
-INSTALL: ALL install date.1
- -mkdir $(TOPDIR) $(BINDIR)
- cp date $(BINDIR)/.
- -mkdir $(TOPDIR) $(MANDIR) $(MANDIR)/man1
- -rm -f $(MANDIR)/man1/date.1
- cp date.1 $(MANDIR)/man1/.
-
-zdump: $(TZDOBJS)
- $(CC) $(CFLAGS) $(LFLAGS) $(TZDOBJS) $(LDLIBS) -o $@
-
-zic: $(TZCOBJS) yearistype
- $(CC) $(CFLAGS) $(LFLAGS) $(TZCOBJS) $(LDLIBS) -o $@
-
-yearistype: yearistype.sh
- cp yearistype.sh yearistype
- chmod +x yearistype
-
-posix_only: zic $(TDATA)
- $(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L /dev/null $(TDATA)
-
-right_only: zic leapseconds $(TDATA)
- $(ZIC) -y $(YEARISTYPE) -d $(TZDIR) -L leapseconds $(TDATA)
-
-# In earlier versions of this makefile, the other two directories were
-# subdirectories of $(TZDIR). However, this led to configuration errors.
-# For example, with posix_right under the earlier scheme,
-# TZ='right/Australia/Adelaide' got you localtime with leap seconds,
-# but gmtime without leap seconds, which led to problems with applications
-# like sendmail that subtract gmtime from localtime.
-# Therefore, the other two directories are now siblings of $(TZDIR).
-# You must replace all of $(TZDIR) to switch from not using leap seconds
-# to using them, or vice versa.
-other_two: zic leapseconds $(TDATA)
- $(ZIC) -y $(YEARISTYPE) -d $(TZDIR)-posix -L /dev/null $(TDATA)
- $(ZIC) -y $(YEARISTYPE) \
- -d $(TZDIR)-leaps -L leapseconds $(TDATA)
-
-posix_right: posix_only other_two
-
-right_posix: right_only other_two
-
-zones: $(REDO)
-
-$(TZLIB): $(LIBOBJS)
- -mkdir $(TOPDIR) $(LIBDIR)
- ar ru $@ $(LIBOBJS)
- if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
- then ranlib $@ ; fi
-
-# We use the system's logwtmp in preference to ours if available.
-
-date: $(DATEOBJS)
- ar r ,lib.a logwtmp.o
- if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
- then ranlib ,lib.a ; fi
- $(CC) $(CFLAGS) date.o localtime.o asctime.o strftime.o \
- $(LDLIBS) -lc ,lib.a -o $@
- rm -f ,lib.a
-
-tzselect: tzselect.ksh
- sed \
- -e 's|AWK=[^}]*|AWK=$(AWK)|g' \
- -e 's|TZDIR=[^}]*|TZDIR=$(TZDIR)|' \
- <$? >$@
- chmod +x $@
-
-check_tables: checktab.awk $(PRIMARY_YDATA)
- $(AWK) -f checktab.awk $(PRIMARY_YDATA)
-
-clean:
- rm -f core *.o *.out tzselect zdump zic yearistype date \
- ,* *.tar.gz
-
-names:
- @echo $(ENCHILADA)
-
-public: $(ENCHILADA)
- tar cf - $(DOCS) $(SOURCES) $(MISC) | gzip -9 > tzcode.tar.gz
- tar cf - $(DATA) | gzip -9 > tzdata.tar.gz
-
-zonenames: $(TDATA)
- @awk '/^Zone/ { print $$2 } /^Link/ { print $$3 }' $(TDATA)
-
-asctime.o: private.h tzfile.h
-date.o: private.h
-difftime.o: private.h
-ialloc.o: private.h
-localtime.o: private.h tzfile.h
-scheck.o: private.h
-strftime.o: tzfile.h
-zic.o: private.h tzfile.h
-
-.KEEP_STATE:
+.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/Music b/usr.sbin/zic/Music
deleted file mode 100644
index 9fb0cec..0000000
--- a/usr.sbin/zic/Music
+++ /dev/null
@@ -1,81 +0,0 @@
-@(#)Music 7.4
-
-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"
-Rating: 1 star
---------------------------------------------------------------------------
-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
-Rating: 1.5 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
-Rating: black dot
---------------------------------------------------------------------------
-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
-Rating: 2 stars
-==========================================================================
-Also of note:
-Artist: Milt Hinton
-CD: Old Man Time
-Date: 1993
-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
- Eddier 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
-Rating: 3 stars
diff --git a/usr.sbin/zic/WWW b/usr.sbin/zic/WWW
deleted file mode 100644
index d2fd684..0000000
--- a/usr.sbin/zic/WWW
+++ /dev/null
@@ -1,71 +0,0 @@
-# '@(#)WWW 7.3'
-
-# From Paul Eggert <eggert@twinsun.com> (1995-11-03)
-#
-# The Web has several other sources for time zone and daylight savings data.
-# Here are some recent links that may be of interest.
-#
-# Date and Time Gateway
-# http://www.bsdi.com/date
-# A text-based source for tables of current time throughout the world.
-# Its point-and-click interface accesses a recent version of the tz data.
-#
-# Local Times Around the World
-# http://www.hilink.com.au/times/
-# This text-based system contains links to local time servers
-# throughout the world, and though the coverage is limited,
-# the live data provide a nice way to check one's tables.
-#
-# World Time Zones
-# http://tycho.usno.navy.mil/tzones.html
-# US Naval Observatory data, used as the source for `usno1995'.
-#
-# Standard Time Zones of the World
-# http://www.odci.gov/cia/publications/95fact/802389.gif [54 kB]
-# http://www.odci.gov/cia/publications/95fact/802389h.gif [1317 kB]
-# A static time zone map, available in both low-resolution and
-# high-resolution versions. The quality is good, but the map does not
-# indicate summer time, and parts of the data are a few years out of date.
-#
-# VIBE's World Map
-# http://pathfinder.com/vibe/vibeworld
-# An active time zone map. You can point to the map and find out what
-# time it is at that location. The map and data are not as good as
-# other sources.
-
-###############################################################################
-
-# From Manavendra Thakur <Manavendra_Thakur@NeXT.COM> (1995-11-06)
-#
-# To Paul's list of time zone information on the web, I would add the
-# following URL:
-# http://www.dhl.com/dhl/dhlinfo/1bb.html
-# or more simply:
-# http://www.dhl.com/
-#
-# This is run by DHL (the courier company), and it presents a list of the
-# countries served by that company. If you then click on a particular
-# country, here's an example of what you'll see (graphics stripped out):
-#
-# United Kingdom
-#
-# HOLIDAYS: Jan 1, 2, Apr 14, 17, May 1, 29, Aug 28, Dec 25, 26
-#
-# INTERNATIONAL DIALING CODE: +44
-#
-# CURRENT LOCAL TIME: 09:41 Monday 6 November 1995
-#
-# I find this rather handy, and given that DHL covers 217 countries and
-# territories, it's pretty comprehensive coverage.
-#
-# (I have no idea what system DHL is using to calculate the local time, but
-# it's been accurate so far.)
-
-###############################################################################
-
-
-# From Arthur David Olson <arthur_david_olson@nih.gov> (1996-01-04)
-#
-# A good source of information about ISO 8601 seems to be
-# http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html
-# maintained by Markus Kuhn.
diff --git a/usr.sbin/zic/ialloc.c b/usr.sbin/zic/ialloc.c
index 8a0c701..a069032 100644
--- a/usr.sbin/zic/ialloc.c
+++ b/usr.sbin/zic/ialloc.c
@@ -1,9 +1,14 @@
#ifndef lint
#ifndef NOID
-static char elsieid[] = "@(#)ialloc.c 8.29";
+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"
diff --git a/usr.sbin/zic/private.h b/usr.sbin/zic/private.h
index c8f4548..585d6e6 100644
--- a/usr.sbin/zic/private.h
+++ b/usr.sbin/zic/private.h
@@ -8,6 +8,15 @@
*/
/*
+ * 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.
@@ -21,7 +30,7 @@
#ifndef lint
#ifndef NOID
-static char privatehid[] = "@(#)private.h 7.53";
+static const char privatehid[] = "@(#)private.h 7.48";
#endif /* !defined NOID */
#endif /* !defined lint */
@@ -30,22 +39,10 @@ static char privatehid[] = "@(#)private.h 7.53";
** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
*/
-#ifndef HAVE_ADJTIME
-#define HAVE_ADJTIME 1
-#endif /* !defined HAVE_ADJTIME */
-
#ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0
#endif /* !defined HAVE_GETTEXT */
-#ifndef HAVE_INCOMPATIBLE_CTIME_R
-#define HAVE_INCOMPATIBLE_CTIME_R 0
-#endif /* !defined INCOMPATIBLE_CTIME_R */
-
-#ifndef HAVE_SETTIMEOFDAY
-#define HAVE_SETTIMEOFDAY 3
-#endif /* !defined HAVE_SETTIMEOFDAY */
-
#ifndef HAVE_STRERROR
#define HAVE_STRERROR 1
#endif /* !defined HAVE_STRERROR */
@@ -54,31 +51,10 @@ static char privatehid[] = "@(#)private.h 7.53";
#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 */
-#ifndef HAVE_UTMPX_H
-#define HAVE_UTMPX_H 0
-#endif /* !defined HAVE_UTMPX_H */
-
-#ifndef LOCALE_HOME
-#define LOCALE_HOME "/usr/lib/locale"
-#endif /* !defined LOCALE_HOME */
-
-#if HAVE_INCOMPATIBLE_CTIME_R
-#define asctime_r _incompatible_asctime_r
-#define ctime_r _incompatible_ctime_r
-#endif /* HAVE_INCOMPATIBLE_CTIME_R */
-
/*
** Nested includes
*/
@@ -95,17 +71,6 @@ static char privatehid[] = "@(#)private.h 7.53";
#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 */
-
-#ifndef WIFEXITED
-#define WIFEXITED(status) (((status) & 0xff) == 0)
-#endif /* !defined WIFEXITED */
-#ifndef WEXITSTATUS
-#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
-#endif /* !defined WEXITSTATUS */
-
#if HAVE_UNISTD_H - 0
#include "unistd.h" /* for F_OK and R_OK */
#endif /* HAVE_UNISTD_H - 0 */
@@ -122,77 +87,7 @@ static char privatehid[] = "@(#)private.h 7.53";
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
-/*
-** Workarounds for compilers/systems.
-*/
-
-/*
-** SunOS 4.1.1 cc lacks prototypes.
-*/
-
-#ifndef P
-#ifdef __STDC__
-#define P(x) x
-#endif /* defined __STDC__ */
-#ifndef __STDC__
-#define P(x) ()
-#endif /* !defined __STDC__ */
-#endif /* !defined P */
-
-/*
-** SunOS 4.1.1 headers lack EXIT_SUCCESS.
-*/
-
-#ifndef EXIT_SUCCESS
-#define EXIT_SUCCESS 0
-#endif /* !defined EXIT_SUCCESS */
-
-/*
-** SunOS 4.1.1 headers lack EXIT_FAILURE.
-*/
-
-#ifndef EXIT_FAILURE
-#define EXIT_FAILURE 1
-#endif /* !defined EXIT_FAILURE */
-
-/*
-** SunOS 4.1.1 headers lack FILENAME_MAX.
-*/
-
-#ifndef FILENAME_MAX
-
-#ifndef MAXPATHLEN
-#ifdef unix
-#include "sys/param.h"
-#endif /* defined unix */
-#endif /* !defined MAXPATHLEN */
-
-#ifdef MAXPATHLEN
-#define FILENAME_MAX MAXPATHLEN
-#endif /* defined MAXPATHLEN */
-#ifndef MAXPATHLEN
-#define FILENAME_MAX 1024 /* Pure guesswork */
-#endif /* !defined MAXPATHLEN */
-
-#endif /* !defined FILENAME_MAX */
-
-/*
-** SunOS 4.1.1 libraries lack remove.
-*/
-
-#ifndef remove
-extern int unlink P((const char * filename));
-#define remove unlink
-#endif /* !defined remove */
-
-/*
-** Some ancient errno.h implementations don't declare errno.
-** But some newer errno.h implementations define it as a macro.
-** Fix the former without affecting the latter.
-*/
-#ifndef errno
-extern int errno;
-#endif /* !defined errno */
+#define P(x) x
/*
** Private function declarations.
@@ -206,7 +101,6 @@ void icfree P((char * pointer));
void ifree P((char * pointer));
char * scheck P((const char *string, const char *format));
-
/*
** Finally, some convenience items.
*/
@@ -280,15 +174,8 @@ char * scheck P((const char *string, const char *format));
#define TZ_DOMAIN "tz"
#endif /* !defined TZ_DOMAIN */
-#if HAVE_INCOMPATIBLE_CTIME_R
-#undef asctime_r
-#undef ctime_r
-char *asctime_r P((struct tm const *, char *));
-char *ctime_r P((time_t const *, char *));
-#endif /* HAVE_INCOMPATIBLE_CTIME_R */
-
/*
-** UNIX was a registered trademark of The Open Group in 2003.
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
*/
#endif /* !defined PRIVATE_H */
diff --git a/usr.sbin/zic/scheck.c b/usr.sbin/zic/scheck.c
index 39feeba..692bf1a 100644
--- a/usr.sbin/zic/scheck.c
+++ b/usr.sbin/zic/scheck.c
@@ -1,9 +1,14 @@
#ifndef lint
#ifndef NOID
-static char elsieid[] = "@(#)scheck.c 8.15";
+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"
diff --git a/usr.sbin/zic/zdump.8 b/usr.sbin/zic/zdump.8
index dff68c2..6961595 100644
--- a/usr.sbin/zic/zdump.8
+++ b/usr.sbin/zic/zdump.8
@@ -1,30 +1,30 @@
-.TH ZDUMP 8
-.SH NAME
-zdump \- time zone dumper
-.SH SYNOPSIS
-.B zdump
-[
-.B \-\-version
-]
-[
-.B \-v
-] [
-.B \-c
-cutoffyear ] [ zonename ... ]
-.SH DESCRIPTION
-.I Zdump
-prints the current time in each
-.I zonename
+.\"
+.\" @(#)zdump.8 7.3
+.\" $FreeBSD$
+.\"
+.Dd September 13, 1994
+.Dt ZDUMP 8
+.Os
+.Sh NAME
+.Nm zdump
+.Nd timezone dumper
+.Sh SYNOPSIS
+.Nm
+.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
-These options are available:
-.TP
-.BI "\-\-version"
-Output version information and exit.
-.TP
-.B \-v
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl v
For each
-.I zonename
+.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,
@@ -33,13 +33,14 @@ 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
-.B isdst=1
+.Em isdst=1
if the given time is Daylight Saving Time or
-.B isdst=0
+.Em isdst=0
otherwise.
-.TP
-.BI "\-c " cutoffyear
+.It Fl c Ar cutoffyear
Cut off the verbose output near the start of the given year.
-.SH "SEE ALSO"
-newctime(3), tzfile(5), zic(8)
-.\" @(#)zdump.8 7.4
+.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
index a34cadd..cfd94ea 100644
--- a/usr.sbin/zic/zdump.c
+++ b/usr.sbin/zic/zdump.c
@@ -1,4 +1,13 @@
-static char elsieid[] = "@(#)zdump.c 7.31";
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)zdump.c 7.28";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
/*
** This code has been made independent of the rest of the time
@@ -6,11 +15,13 @@ static char elsieid[] = "@(#)zdump.c 7.31";
** You can use this code to help in verifying other implementations.
*/
-#include "stdio.h" /* for stdout, stderr, perror */
-#include "string.h" /* for strcpy */
-#include "sys/types.h" /* for time_t */
-#include "time.h" /* for struct tm */
-#include "stdlib.h" /* for exit, malloc, atoi */
+#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
@@ -117,18 +128,14 @@ static char elsieid[] = "@(#)zdump.c 7.31";
#endif /* !defined P */
extern char ** environ;
-extern int getopt P((int argc, char * const argv[],
- const char * options));
-extern char * optarg;
-extern int optind;
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 char * progname;
static void show P((char * zone, time_t t, int v));
+static void usage(void);
int
main(argc, argv)
@@ -157,12 +164,6 @@ char * argv[];
#endif /* defined(TEXTDOMAINDIR) */
(void) textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT - 0 */
- progname = argv[0];
- for (i = 1; i < argc; ++i)
- if (strcmp(argv[i], "--version") == 0) {
- (void) printf("%s\n", elsieid);
- (void) exit(EXIT_SUCCESS);
- }
vflag = 0;
cutoff = NULL;
while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
@@ -171,10 +172,7 @@ char * argv[];
else cutoff = optarg;
if ((c != EOF && c != -1) ||
(optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
- (void) fprintf(stderr,
-_("%s: usage is %s [ --version ] [ -v ] [ -c cutoff ] zonename ...\n"),
- argv[0], argv[0]);
- (void) exit(EXIT_FAILURE);
+ usage();
}
if (cutoff != NULL) {
int y;
@@ -201,10 +199,10 @@ _("%s: usage is %s [ --version ] [ -v ] [ -c cutoff ] zonename ...\n"),
fakeenv = (char **) malloc((size_t) ((i + 2) *
sizeof *fakeenv));
if (fakeenv == NULL ||
- (fakeenv[0] = (char *) malloc(longest + 4)) == NULL) {
- (void) perror(progname);
- (void) exit(EXIT_FAILURE);
- }
+ (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)
@@ -263,11 +261,8 @@ _("%s: usage is %s [ --version ] [ -v ] [ -c cutoff ] zonename ...\n"),
t += SECSPERHOUR * HOURSPERDAY;
show(argv[i], t, TRUE);
}
- if (fflush(stdout) || ferror(stdout)) {
- (void) fprintf(stderr, "%s: ", argv[0]);
- (void) perror(_("Error writing standard output"));
- (void) exit(EXIT_FAILURE);
- }
+ if (fflush(stdout) || ferror(stdout))
+ errx(EXIT_FAILURE, _("error writing standard output"));
exit(EXIT_SUCCESS);
/* gcc -Wall pacifier */
@@ -275,6 +270,13 @@ _("%s: usage is %s [ --version ] [ -v ] [ -c cutoff ] zonename ...\n"),
continue;
}
+static void
+usage(void)
+{
+ fprintf(stderr, _("usage: zdump [-v] [-c cutoff] zonename ...\n"));
+ exit(EXIT_FAILURE);
+}
+
static time_t
hunt(name, lot, hit)
char * name;
diff --git a/usr.sbin/zic/zdump/Makefile b/usr.sbin/zic/zdump/Makefile
new file mode 100644
index 0000000..127946f
--- /dev/null
+++ b/usr.sbin/zic/zdump/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zdump
+MAN= ${.CURDIR}/../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
index 86b1354..5355c80 100644
--- a/usr.sbin/zic/zic.8
+++ b/usr.sbin/zic/zic.8
@@ -1,103 +1,109 @@
-.TH ZIC 8
-.SH NAME
-zic \- time zone compiler
-.SH SYNOPSIS
-.B zic
-[
-.B \-\-version
-]
-[
-.B \-v
-] [
-.B \-d
-.I directory
-] [
-.B \-l
-.I localtime
-] [
-.B \-p
-.I posixrules
-] [
-.B \-L
-.I leapsecondfilename
-] [
-.B \-s
-] [
-.B \-y
-.I command
-] [
-.I filename
-\&... ]
-.SH DESCRIPTION
-.if t .ds lq ``
-.if t .ds rq ''
-.if n .ds lq \&"\"
-.if n .ds rq \&"\"
-.de q
-\\$3\*(lq\\$1\*(rq\\$2
-..
-.I Zic
-reads text from the file(s) named on the command line
+.\" $FreeBSD$
+.Dd October 29, 1997
+.Dt ZIC 8
+.Os
+.Sh NAME
+.Nm zic
+.Nd timezone compiler
+.Sh SYNOPSIS
+.Nm
+.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
-.I filename
+.Ar filename
is
-.BR \- ,
+.Em - ,
the standard input is read.
-.PP
-These options are available:
-.TP
-.BI "\-\-version"
-Output version information and exit.
-.TP
-.BI "\-d " directory
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.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.
-.TP
-.BI "\-l " timezone
-Use the given time zone as local time.
-.I Zic
-will act as if the input contained a link line of the form
-.sp
-.ti +.5i
-Link \fItimezone\fP localtime
-.TP
-.BI "\-p " timezone
-Use the given time zone's rules when handling POSIX-format
-time zone environment variables.
-.I Zic
-will act as if the input contained a link line of the form
-.sp
-.ti +.5i
-Link \fItimezone\fP posixrules
-.TP
-.BI "\-L " leapsecondfilename
+.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.
-.TP
-.B \-v
+.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
+.Bd -literal -offset indent
+.No "Link timezone localtime
+.Ed
+(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
+.Bd -literal -offset indent
+.No "Link timezone posixrules
+.Ed
+.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
-.IR time (2)
+.Xr time 3
values.
-Also complain if a time of 24:00
-(which cannot be handled by pre-1998 versions of
-.IR zic )
-appears in the input.
-.TP
-.B \-s
+.It Fl s
Limit time values stored in output files to values that are the same
whether they're taken to be signed or unsigned.
You can use this option to generate SVVS-compatible files.
-.TP
-.BI "\-y " command
+.It Fl y Ar command
Use the given
-.I command
+.Ar command
rather than
-.B yearistype
+.Em yearistype
when checking year types (see below).
-.PP
+.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.
@@ -108,210 +114,182 @@ White space characters and sharp characters may be enclosed in double quotes
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
-.nf
-.ti +.5i
-.ta \w'Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u
-.sp
-Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
-.sp
+.Pp
+A rule line has the form:
+.Dl "Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
For example:
-.ti +.5i
-.sp
-Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D
-.sp
-.fi
+.Dl "Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D
+.Pp
The fields that make up a rule line are:
-.TP "\w'LETTER/S'u"
-.B NAME
-Gives the (arbitrary) name of the set of rules this rule is part of.
-.TP
-.B FROM
-Gives the first year in which the rule applies.
+.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
-.B minimum
+.Em minimum
(or an abbreviation) means the minimum year representable as an integer.
The word
-.B maximum
+.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.
-.TP
-.B TO
-Gives the final year in which the rule applies.
+.It TO
+Give the final year in which the rule applies.
In addition to
-.B minimum
+.Em minimum
and
-.B maximum
+.Em maximum
(as above),
the word
-.B only
+.Em only
(or an abbreviation)
may be used to repeat the value of the
-.B FROM
+.Em FROM
field.
-.TP
-.B TYPE
-Gives the type of year in which the rule applies.
+.It TYPE
+Give the type of year in which the rule applies.
If
-.B TYPE
+.Em TYPE
is
-.B \-
+.Em \-
then the rule applies in all years between
-.B FROM
+.Em FROM
and
-.B TO
+.Em TO
inclusive.
If
-.B TYPE
+.Em TYPE
is something else, then
-.I zic
+.Nm
executes the command
-.ti +.5i
-\fByearistype\fP \fIyear\fP \fItype\fP
-.br
+.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.
-.TP
-.B IN
-Names the month in which the rule takes effect.
+.It IN
+Name the month in which the rule takes effect.
Month names may be abbreviated.
-.TP
-.B ON
-Gives the day on which the rule takes effect.
+.It ON
+Give the day on which the rule takes effect.
Recognized forms include:
-.nf
-.in +.5i
-.sp
-.ta \w'Sun<=25\0\0'u
-5 the fifth of the month
-lastSun the last Sunday in the month
-lastMon the last Monday in the month
-Sun>=8 first Sunday on or after the eighth
-Sun<=25 last Sunday on or before the 25th
-.fi
-.in -.5i
-.sp
+.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
-.B ON
+.Em ON
field.
-.TP
-.B AT
-Gives the time of day at which the rule takes effect.
+.It AT
+Give the time of day at which the rule takes effect.
Recognized forms include:
-.nf
-.in +.5i
-.sp
-.ta \w'1:28:13\0\0'u
-2 time in hours
-2:00 time in hours and minutes
-15:00 24-hour format time (for times after noon)
-1:28:14 time in hours, minutes, and seconds
-\- equivalent to 0
-.fi
-.in -.5i
-.sp
+.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
-.B w
+.Sq Li w
if the given time is local
-.q "wall clock"
+.Dq "wall clock"
time,
-.B s
+.Sq Li s
if the given time is local
-.q standard
+.Dq standard
time, or
-.B u
+.Sq Li u
(or
-.B g
+.Sq Li g
or
-.BR z )
+.Sq Li z )
if the given time is universal time;
in the absence of an indicator,
wall clock time is assumed.
-.TP
-.B SAVE
-Gives the amount of time to be added to local standard time when the rule is in
+.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
-.B AT
+.Em AT
field
(although, of course, the
-.B w
+.Sq Li w
and
-.B s
+.Sq Li s
suffixes are not used).
-.TP
-.B LETTER/S
-Gives the
-.q "variable part"
+.It LETTER/S
+Give the
+.Dq "variable part"
(for example, the
-.q S
+.Dq S
or
-.q D
+.Dq D
in
-.q EST
+.Dq EST
or
-.q EDT )
+.Dq EDT )
of time zone abbreviations to be used when this rule is in effect.
If this field is
-.BR \- ,
+.Em \- ,
the variable part is null.
-.PP
-A zone line has the form
-.sp
-.nf
-.ti +.5i
-.ta \w'Zone\0\0'u +\w'Australia/Adelaide\0\0'u +\w'GMTOFF\0\0'u +\w'RULES/SAVE\0\0'u +\w'FORMAT\0\0'u
-Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]
-.sp
+.El
+.Pp
+A zone line has the form:
+.Dl "Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]
For example:
-.sp
-.ti +.5i
-Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00
-.sp
-.fi
+.Dl "Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00
The fields that make up a zone line are:
-.TP "\w'GMTOFF'u"
-.B NAME
+.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.
-.TP
-.B GMTOFF
+.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
-.B AT
+.Em AT
and
-.B SAVE
+.Em SAVE
fields of rule lines;
begin the field with a minus sign if time must be subtracted from UTC.
-.TP
-.B RULES/SAVE
+.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
-.B \-
+.Em \-
then standard time always applies in the time zone.
-.TP
-.B FORMAT
+.It FORMAT
The format for time zone abbreviations in this time zone.
The pair of characters
-.B %s
+.Em %s
is used to show where the
-.q "variable part"
+.Dq "variable part"
of the time zone abbreviation goes.
Alternately,
a slash (/)
separates standard and daylight abbreviations.
-.TP
-.B UNTIL
+.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,
@@ -320,74 +298,57 @@ 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.
-.IP
+.Pp
The next line must be a
-.q continuation
+.Dq continuation
line; this has the same form as a zone line except that the
string
-.q Zone
+.Dq Zone
and the name are omitted, as the continuation line will
place information starting at the time specified as the
-.B UNTIL
+.Em UNTIL
field in the previous line in the file used by the previous line.
Continuation lines may contain an
-.B UNTIL
+.Em UNTIL
field, just as zone lines do, indicating that the next line is a further
continuation.
-.PP
+.El
+.Pp
A link line has the form
-.sp
-.nf
-.ti +.5i
-.ta \w'Link\0\0'u +\w'Europe/Istanbul\0\0'u
-Link LINK-FROM LINK-TO
-.sp
+.Dl "Link LINK-FROM LINK-TO
For example:
-.sp
-.ti +.5i
-Link Europe/Istanbul Asia/Istanbul
-.sp
-.fi
+.Dl "Link Europe/Istanbul Asia/Istanbul
The
-.B LINK-FROM
+.Em LINK-FROM
field should appear as the
-.B NAME
+.Em NAME
field in some zone line;
the
-.B LINK-TO
+.Em LINK-TO
field is used as an alternate name for that zone.
-.PP
+.Pp
Except for continuation lines,
lines may appear in any order in the input.
-.PP
+.Pp
Lines in the file that describes leap seconds have the following form:
-.nf
-.ti +.5i
-.ta \w'Leap\0\0'u +\w'YEAR\0\0'u +\w'MONTH\0\0'u +\w'DAY\0\0'u +\w'HH:MM:SS\0\0'u +\w'CORR\0\0'u
-.sp
-Leap YEAR MONTH DAY HH:MM:SS CORR R/S
-.sp
+.Dl "Leap YEAR MONTH DAY HH:MM:SS CORR R/S
For example:
-.ti +.5i
-.sp
-Leap 1974 Dec 31 23:59:60 + S
-.sp
-.fi
+.Dl "Leap 1974 Dec 31 23:59:60 + S
The
-.BR YEAR ,
-.BR MONTH ,
-.BR DAY ,
+.Em YEAR ,
+.Em MONTH ,
+.Em DAY ,
and
-.B HH:MM:SS
+.Em HH:MM:SS
fields tell when the leap second happened.
The
-.B CORR
+.Em CORR
field
should be
-.q +
+.Dq +
if a second was added
or
-.q -
+.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.
@@ -401,24 +362,29 @@ if a second was skipped.
.\" .q --
.\" if two seconds were skipped.
The
-.B R/S
+.Em R/S
field
should be (an abbreviation of)
-.q Stationary
+.Dq Stationary
if the leap second time given by the other fields should be interpreted as UTC
or
(an abbreviation of)
-.q Rolling
+.Dq Rolling
if the leap second time given by the other fields should be interpreted as
local wall clock time.
-.SH NOTE
+.Sh NOTE
For areas with more than two types of local time,
you may need to use local standard time in the
-.B AT
+.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 FILE
-/usr/local/etc/zoneinfo standard directory used for created files
-.SH "SEE ALSO"
-newctime(3), tzfile(5), zdump(8)
-.\" @(#)zic.8 7.22
+.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
index 1a046ff..5492648 100644
--- a/usr.sbin/zic/zic.c
+++ b/usr.sbin/zic/zic.c
@@ -1,23 +1,27 @@
-static char elsieid[] = "@(#)zic.c 7.116";
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)zic.c 7.96";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
#include "private.h"
-#include "locale.h"
#include "tzfile.h"
-
-#if HAVE_SYS_STAT_H
-#include "sys/stat.h"
-#endif
-#ifdef S_IRUSR
-#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
-#else
-#define MKDIR_UMASK 0755
-#endif
+#include <err.h>
+#include <locale.h>
+#include <sys/stat.h> /* for umask manifest constants */
+#include <sys/types.h>
+#include <unistd.h>
/*
** 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.
+** 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.
*/
@@ -79,12 +83,6 @@ struct zone {
time_t z_untiltime;
};
-extern int getopt P((int argc, char * const argv[],
- const char * options));
-extern int link P((const char * fromname, const char * toname));
-extern char * optarg;
-extern int optind;
-
static void addtt P((time_t starttime, int type));
static int addtype P((long gmtoff, const char * abbr, int isdst,
int ttisstd, int ttisgmt));
@@ -127,6 +125,8 @@ static void rulesub P((struct rule * rp,
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));
@@ -150,7 +150,6 @@ static int min_year_representable;
static int noise;
static const char * rfilename;
static int rlinenum;
-static const char * progname;
static int timecnt;
static int typecnt;
@@ -355,13 +354,8 @@ static char *
memcheck(ptr)
char * const ptr;
{
- if (ptr == NULL) {
- const char *e = strerror(errno);
-
- (void) fprintf(stderr, _("%s: Memory exhausted: %s\n"),
- progname, e);
- (void) exit(EXIT_FAILURE);
- }
+ if (ptr == NULL)
+ errx(EXIT_FAILURE, _("memory exhausted"));
return ptr;
}
@@ -442,8 +436,9 @@ const char * const string;
static void
usage P((void))
{
- (void) fprintf(stderr, _("%s: usage is %s [ --version ] [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"),
- progname, progname);
+ (void) fprintf(stderr, "%s\n%s\n",
+_("usage: zic [-s] [-v] [-l localtime] [-p posixrules] [-d directory]"),
+_(" [-L leapseconds] [-y yearistype] [filename ... ]"));
(void) exit(EXIT_FAILURE);
}
@@ -453,6 +448,11 @@ 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)
@@ -473,65 +473,63 @@ char * argv[];
#endif /* defined TEXTDOMAINDIR */
(void) textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT - 0 */
- progname = argv[0];
- for (i = 1; i < argc; ++i)
- if (strcmp(argv[i], "--version") == 0) {
- (void) printf("%s\n", elsieid);
- (void) exit(EXIT_SUCCESS);
- }
- while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1)
+ 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 {
- (void) fprintf(stderr,
-_("%s: More than one -d option specified\n"),
- progname);
- (void) exit(EXIT_FAILURE);
- }
+ 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 {
- (void) fprintf(stderr,
-_("%s: More than one -l option specified\n"),
- progname);
- (void) exit(EXIT_FAILURE);
- }
+ 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"));
+ getmode(set, mflag);
+ free(set);
break;
+ }
case 'p':
if (psxrules == NULL)
psxrules = optarg;
- else {
- (void) fprintf(stderr,
-_("%s: More than one -p option specified\n"),
- progname);
- (void) exit(EXIT_FAILURE);
- }
+ 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 {
- (void) fprintf(stderr,
-_("%s: More than one -y option specified\n"),
- progname);
- (void) exit(EXIT_FAILURE);
- }
+ else
+ errx(EXIT_FAILURE,
+_("more than one -y option specified"));
break;
case 'L':
if (leapsec == NULL)
leapsec = optarg;
- else {
- (void) fprintf(stderr,
-_("%s: More than one -L option specified\n"),
- progname);
- (void) exit(EXIT_FAILURE);
- }
+ else
+ errx(EXIT_FAILURE,
+_("more than one -L option specified"));
break;
case 'v':
noise = TRUE;
@@ -570,18 +568,12 @@ _("%s: More than one -L option specified\n"),
/*
** Make links.
*/
- for (i = 0; i < nlinks; ++i) {
- eat(links[i].l_filename, links[i].l_linenum);
+ for (i = 0; i < nlinks; ++i)
dolink(links[i].l_from, links[i].l_to);
- }
- if (lcltime != NULL) {
- eat("command line", 1);
+ if (lcltime != NULL)
dolink(lcltime, TZDEFAULT);
- }
- if (psxrules != NULL) {
- eat("command line", 1);
+ if (psxrules != NULL)
dolink(psxrules, TZDEFRULES);
- }
return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
@@ -618,31 +610,17 @@ const char * const tofile;
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 (HAVE_SYMLINK - 0)
+ if (result != 0) {
+ result = symlink(fromname, toname);
if (result == 0)
warning(_("hard link failed, symbolic link used"));
- ifree(symlinkcontents);
}
#endif
if (result != 0) {
- const char *e = strerror(errno);
-
- (void) fprintf(stderr,
- _("%s: Can't link from %s to %s: %s\n"),
- progname, fromname, toname, e);
- (void) exit(EXIT_FAILURE);
+ err(EXIT_FAILURE, _("can't link from %s to %s"),
+ fromname, toname);
}
}
ifree(fromname);
@@ -811,13 +789,8 @@ const char * name;
if (strcmp(name, "-") == 0) {
name = _("standard input");
fp = stdin;
- } else if ((fp = fopen(name, "r")) == NULL) {
- const char *e = strerror(errno);
-
- (void) fprintf(stderr, _("%s: Can't open %s: %s\n"),
- progname, name, e);
- (void) exit(EXIT_FAILURE);
- }
+ } else if ((fp = fopen(name, "r")) == NULL)
+ err(EXIT_FAILURE, _("can't open %s"), name);
wantcont = FALSE;
for (num = 1; ; ++num) {
eat(name, num);
@@ -860,33 +833,22 @@ const char * name;
break;
case LC_LEAP:
if (name != leapsec)
- (void) fprintf(stderr,
-_("%s: Leap line in non leap seconds file %s\n"),
- progname, name);
+ warnx(
+_("leap line in non leap seconds file %s"), name);
else inleap(fields, nfields);
wantcont = FALSE;
break;
default: /* "cannot happen" */
- (void) fprintf(stderr,
-_("%s: panic: Invalid l_value %d\n"),
- progname, lp->l_value);
- (void) exit(EXIT_FAILURE);
+ errx(EXIT_FAILURE,
+_("panic: invalid l_value %d"), lp->l_value);
}
}
ifree((char *) fields);
}
- if (ferror(fp)) {
- (void) fprintf(stderr, _("%s: Error reading %s\n"),
- progname, filename);
- (void) exit(EXIT_FAILURE);
- }
- if (fp != stdin && fclose(fp)) {
- const char *e = strerror(errno);
-
- (void) fprintf(stderr, _("%s: Error closing %s: %s\n"),
- progname, filename, e);
- (void) exit(EXIT_FAILURE);
- }
+ 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"));
}
@@ -931,8 +893,6 @@ const int signable;
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));
@@ -1157,15 +1117,14 @@ const int nfields;
error(_("time before zero"));
return;
}
- if (dayoff < min_time / SECSPERDAY) {
- error(_("time too small"));
- return;
- }
- if (dayoff > max_time / SECSPERDAY) {
- error(_("time too large"));
+ t = (time_t) dayoff * SECSPERDAY;
+ /*
+ ** Cheap overflow check.
+ */
+ if (t / SECSPERDAY != dayoff) {
+ error(_("time overflow"));
return;
}
- t = (time_t) dayoff * SECSPERDAY;
tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
cp = fields[LP_CORR];
{
@@ -1284,10 +1243,8 @@ const char * const timep;
rp->r_loyear = INT_MAX;
break;
default: /* "cannot happen" */
- (void) fprintf(stderr,
- _("%s: panic: Invalid l_value %d\n"),
- progname, lp->l_value);
- (void) exit(EXIT_FAILURE);
+ 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;
@@ -1309,18 +1266,16 @@ const char * const timep;
rp->r_hiyear = rp->r_loyear;
break;
default: /* "cannot happen" */
- (void) fprintf(stderr,
- _("%s: panic: Invalid l_value %d\n"),
- progname, lp->l_value);
- (void) exit(EXIT_FAILURE);
+ 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"));
+ warning(_("starting year too low to be represented"));
else if (rp->r_loyear > max_year_representable)
- warning(_("ending year too high to be represented"));
+ warning(_("starting year too high to be represented"));
}
if (rp->r_loyear > rp->r_hiyear) {
error(_("starting year greater than ending year"));
@@ -1476,26 +1431,18 @@ const char * const name;
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) {
- const char *e = strerror(errno);
+ * Remove old file, if any, to snap links.
+ */
+ if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
+ err(EXIT_FAILURE, _("can't remove %s"), fullname);
- (void) fprintf(stderr, _("%s: Can't remove %s: %s\n"),
- progname, fullname, e);
- (void) exit(EXIT_FAILURE);
- }
if ((fp = fopen(fullname, "wb")) == NULL) {
if (mkdirs(fullname) != 0)
(void) exit(EXIT_FAILURE);
- if ((fp = fopen(fullname, "wb")) == NULL) {
- const char *e = strerror(errno);
-
- (void) fprintf(stderr, _("%s: Can't create %s: %s\n"),
- progname, fullname, e);
- (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);
@@ -1557,11 +1504,15 @@ const char * const name;
(void) putc(ttisstds[i], fp);
for (i = 0; i < typecnt; ++i)
(void) putc(ttisgmts[i], fp);
- if (ferror(fp) || fclose(fp)) {
- (void) fprintf(stderr, _("%s: Error writing %s\n"),
- progname, fullname);
- (void) exit(EXIT_FAILURE);
- }
+ 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
@@ -1611,16 +1562,16 @@ const int zonecount;
typecnt = 0;
charcnt = 0;
/*
+ ** A guess that may well be corrected later.
+ */
+ stdoff = 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);
@@ -1640,7 +1591,8 @@ const int zonecount;
if (usestart) {
addtt(starttime, type);
usestart = FALSE;
- } else if (stdoff != 0)
+ }
+ 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)
@@ -1917,15 +1869,12 @@ const char * const type;
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"));
- (void) fprintf(stderr, _("%s: command was '%s', result was %d\n"),
- progname, buf, result);
+ if (result == 0)
+ return TRUE;
+ if (result == (1 << 8))
+ return FALSE;
+ error(_("wild result from command execution"));
+ warnx(_("command was '%s', result was %d"), buf, result);
for ( ; ; )
(void) exit(EXIT_FAILURE);
}
@@ -2019,7 +1968,7 @@ register char * cp;
else while ((*dp = *cp++) != '"')
if (*dp != '\0')
++dp;
- else error(_("Odd number of quotation marks"));
+ else error(_("odd number of quotation marks"));
} while (*cp != '\0' && *cp != '#' &&
(!isascii(*cp) || !isspace((unsigned char) *cp)));
if (isascii(*cp) && isspace((unsigned char) *cp))
@@ -2139,17 +2088,18 @@ register const int wantedy;
--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"));
+ error(_("no day in month matches rule"));
+ (void) exit(EXIT_FAILURE);
}
}
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;
+ /*
+ ** Cheap overflow check.
+ */
+ if (t / SECSPERDAY != dayoff)
+ return (dayoff > 0) ? max_time : min_time;
return tadd(t, rp->r_tod);
}
@@ -2175,7 +2125,7 @@ char * const argname;
register char * name;
register char * cp;
- if (argname == NULL || *argname == '\0')
+ if (argname == NULL || *argname == '\0' || Dflag)
return 0;
cp = name = ecpyalloc(argname);
while ((cp = strchr(cp + 1, '/')) != 0) {
@@ -2197,16 +2147,13 @@ char * const argname;
** created by some other multiprocessor, so we get
** to do extra checking.
*/
- if (mkdir(name, MKDIR_UMASK) != 0) {
- const char *e = strerror(errno);
-
- if (errno != EEXIST || !itsdir(name)) {
- (void) fprintf(stderr,
-_("%s: Can't create directory %s: %s\n"),
- progname, name, e);
- ifree(name);
- return -1;
- }
+ if (mkdir(name, (S_IRUSR | S_IWUSR | S_IXUSR
+ | S_IRGRP | S_IXGRP | S_IROTH
+ | S_IXOTH)) != 0
+ && (errno != EEXIST || !itsdir(name))) {
+ warn(_("can't create directory %s"), name);
+ ifree(name);
+ return -1;
}
}
*cp = '/';
@@ -2222,15 +2169,64 @@ const int i;
long l;
l = i;
- if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
- (void) fprintf(stderr,
- _("%s: %d did not sign extend correctly\n"),
- progname, i);
- (void) exit(EXIT_FAILURE);
- }
+ 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.
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
*/
diff --git a/usr.sbin/zic/zic/Makefile b/usr.sbin/zic/zic/Makefile
new file mode 100644
index 0000000..c98799cf
--- /dev/null
+++ b/usr.sbin/zic/zic/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zic
+MAN= ${.CURDIR}/../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